Skip to content

Development

monitoreo-ugr edited this page Nov 6, 2026 · 4 revisions

Versión en español

Development

For those who change the code of MonitorEO or publish its versions. To use the tools, start with Getting started.

Languages of the texts

The texts of the tools (QGIS plugin, assistant for managers, Earth Engine app, results viewer, reports, installers, command line, R and Python packages, MCP server) are in English; the documentation is in Spanish and English. Each wiki page has its English page (and the README has both languages). So the new texts of a tool are written in English, and the new sections of the manual are written in both languages. The assistant for managers keeps answering in the language of each question.

Where each tool is

They all use the same functions (monitoreo.services), so the same computation gives the same result in all of them; the Earth Engine app is the JavaScript translation of the core of the package.

Tool Folder of the repository Manual
Earth Engine app gee Earth Engine app
Python package monitoreo and command line src/monitoreo (cli.py) Python package, Command line
QGIS plugin MonitorEO qgis/monitoreo_qgis QGIS plugin
MCP server src/monitoreo/mcp_server.py MCP server
Assistant for managers src/monitoreo/assistant.py (web server, window and MCP client), ui/assistant.html (the page, with Leaflet in ui/vendor), report.py (the report and the map views), engine.py (Ollama) and manual.py (search in this wiki) Assistant
R package monitoreo r/monitoreo R package
Results viewer src/monitoreo/viewer, monitoreo publish Results viewer

Everything is computed in Google Earth Engine: with the account and project of each user or, without a project, with the shared account of MonitorEO (the app, always with the project of MonitorEO). The only server is the small broker of the shared account, in Cloud Run.

Catalog

src/monitoreo/catalog.json is the catalog of variables and is maintained by hand in this repository: collection, formula, scale and offset of the bands, unit, quality bands and colour ramp of each one. catalog.EXTRA adds the NBR of Sentinel-2 and Landsat (fires). The Earth Engine app carries a copy, written by gee/build.py.

Details of the computation, in the code:

  • FIXES in catalog.py: the scale of the LAI of MCD15A3H is 0.1, not 0.01.
  • The scale and offset of the bands are applied before the formula (EVI, the albedo and the chlorophyll of Sentinel-2 need reflectances, not the unscaled bands).
  • The band names of the formulas are compared case-insensitively (LST_day_1km of entries 15 and 16).
  • The MODIS flag values (e.g. 250 = cloud in NDSI_Snow_Cover) are always discarded.
  • Landsat 7 is only used until 31-5-2003, as the catalog says.
  • WDPA changed its schema in Earth Engine: the ID is now SITE_ID (before, WDPAID).

Tests

pytest tests/test_offline.py tests/test_publish.py tests/test_assistant.py   # without Earth Engine or network
EE_PROJECT=my-project pytest -m ee                  # with Earth Engine (the gee app, with npm install in gee)

The tests use the code of src/ of the clone even if the environment has the GitHub version installed. If there are packages installed with pip install --user, add PYTHONNOUSERSITE=1 (see Troubleshooting).

To test the command line or the MCP server with unpublished changes: PYTHONPATH=src python -m monitoreo.cli mcp --no-update (or pip install -e . in a separate environment, which neither monitoreo mcp nor eHabitat+ update). The assistant, the same way: PYTHONPATH=src python -m monitoreo.cli assistant --no-update.

In the assistant, the model only sees the answers of list_variables, describe_variable and find_protected_areas (ADVICE in assistant.py) and those of the exports to Google Drive and task_status (TASKS, without values: the identifier and the state of the task); those of the computation and download tools (RESULTS) go to the page and the report, and the model receives an acknowledgement without values. The manual tool is not of the MCP server: the assistant answers it with manual.search, which searches this wiki (downloaded from raw.githubusercontent.com/wiki/... to ~/MonitorEO/wiki); so what is written here is what the assistant knows about MonitorEO. OTHER_TOOLS recognizes the requests for other tools, and fix_links changes the links to the wiki that do not exist. tests/test_assistant.py checks it with a test model. The instructions of the model (PROMPT, with the guide of sensors and the list of variables of the catalog) are in the same file and are updated with the package.

The page opens in its own window with pywebview (WebKit on macOS, WebView2 on Windows, Qt WebEngine or GTK on Linux): run starts the server in a thread and the window in the main one, and closing it stops the server (if a computation is running, pywebview asks first: the closing event sets confirm_close, and WINDOW_TEXTS translates the question and the macOS menu; the page then does not show Close the assistant); without pywebview, or if it cannot open it, it uses the browser (--browser forces it). The update at startup (_update_in_window in cli.py) shows a small window while pip installs and then starts the assistant in another process, because pywebview only starts once in each. The maps of the page use Leaflet 1.9.4, copied in ui/vendor/leaflet (BSD-2 license) so that it works without depending on a CDN, with the Earth Engine tiles of map_layer, trend_map and compare_map; the results over regions (PERIOD_MAP) also bring the map of the median of their period, or of their last year if it is longer (Assistant.period_map: map_layer with quick=True, only the tiles, without preview and with the colour ramp of the catalog, because computing the percentiles of the median of years of images over a large space is slow or exhausts the memory of Earth Engine; quick is hidden from the model like the arguments of HIDDEN). The maps of each step (steps.map_steps, with the windows of composite.windows and composite.bins, the same ones as the series) are made when they are chosen in the page (/api/map/step, Assistant.step_map), with quick and the vmin and vmax of the map of the result. The QGIS plugin offers the same steps in the windows of its series and trends (result_maps in dock.py), but as GeoTIFF (download_image and download_trend), with the ramp of the first map of the result applied in QGIS. If the percentiles fail in any map, maps.vis_params also uses the ramp of the catalog. The value and the series of a pixel are region_stats and time_series with {"point": [lon, lat]}. Add this view to the report composes on the server (report.map_view, with Pillow) the background tiles and the Earth Engine ones of the frame, with the pixel marked and the credits, in a PNG that goes inside the report.

The installers are in installer/: macos/instalar.sh, linux/instalar.sh and windows/instalar.bat (saved with Windows line endings, -text in .gitattributes, and without accents). They install the environment.yml of main with micromamba in ~/MonitorEO/entorno, create the Desktop shortcut and open the assistant with python -s -m monitoreo.cli assistant, without a terminal: on macOS, the app MonitorEO Assistant.app (an Info.plist and a script; since the computer itself creates it, it is not quarantined and does not need to be signed); on Windows, a shortcut to pythonw.exe. Without a terminal, the messages go to ~/MonitorEO/assistant-output.log. They are downloaded from raw.githubusercontent.com with the one-line commands of Assistant (what curl downloads is not quarantined on macOS nor given the Internet mark on Windows, so they are not blocked for being unsigned) or as a zip: the workflow .github/workflows/installers.yml compresses them (asistente-monitoreo-macos.zip, with Install the MonitorEO assistant.command with the execute permission, and asistente-monitoreo-windows.zip, with Install the MonitorEO assistant.bat) and attaches them to each release; the wiki links them at releases/latest/download/. To attach them to a release that already exists: Actions > Assistant installers > Run workflow, with its tag. For macOS and Windows to open the zips without warnings they would have to be signed (on macOS, with an Apple Developer ID certificate and its notarization). To test the macOS one without touching your folder: HOME=/tmp/test bash installer/macos/instalar.sh (the app it opens at the end uses your real HOME, because macOS does not pass it the one of the command: close it).

The Windows one checks that micromamba.exe starts: it needs the Visual C++ runtime (MSVCP140.dll, VCRUNTIME140.dll...), which a newly installed Windows does not have (the error 0xC0000135). If it is missing, it copies next to micromamba.exe the DLLs of the package vc14_runtime of conda-forge (a .conda: a zip with a .tar.zst, which the tar of Windows 11 opens), only if Microsoft signs them (Get-AuthenticodeSignature), without installing anything in the system. It installs with CONDA_PKGS_DIRS=%SystemDrive%\MonitorEO-pkgs, which it deletes at the end: micromamba keeps the packages under the address of their channel (pkgs\https\conda.anaconda.org\conda-forge\win-64\...) and, in the folder of the user, some paths pass the 260 characters of MAX_PATH (the long paths of Windows need an administrator); there the longest has 234 with any user name. For the same reason the environment has matplotlib-base and not matplotlib, which on Windows brings PySide6 and Qt (qt6-main, with paths of 261 characters for a user name of 6 letters), which it does not use. The Desktop shortcut opens the assistant with pythonw.exe, without a console: update._pip gives pip a stderr (its stdout), because otherwise subprocess would give it a closed pipe, where writing makes it end with the status 120 even when it has installed the package. It was tested in a newly installed Windows 11 in a virtual machine (UTM on a Mac), with an administrator user.

The icon of the app, of the Windows shortcut and of the Ubuntu launcher is the logo of the QGIS plugin (qgis/monitoreo_qgis/icon.png, of 64 px) drawn again at a larger size, with its geometry and its colours and transparent corners: installer/icons/make_icons.py (with Pillow; the .icns, only on a Mac, with iconutil) writes monitoreo-assistant.icns, .ico and .png in installer/icons, from where the installers download them.

The Ubuntu one (linux/instalar.sh, for 22.04, 24.04 and 26.04 LTS, on x86_64 and aarch64) is downloaded with wget, which Ubuntu always has (curl not), and is not zipped: the file manager of Ubuntu does not run scripts. It adds pyqt6-webengine and qtpy to the environment.yml, because pywebview needs Qt WebEngine on Linux (the environment has no Qt: it has matplotlib-base, matplotlib without it), and writes the launcher entorno/bin/monitoreo-assistant, which ~/.local/share/applications/monitoreo-assistant.desktop and its copy on the Desktop open (trusted with gio set ... metadata::trusted true, so that Ubuntu opens it). The launcher chooses PyQt6 (QT_API), X11 (XWayland on Wayland) and RESOURCE_NAME=monitoreo-assistant, which gives the window the WM_CLASS of the .desktop (else it is cli.py and the dock does not match it to its icon), and removes the sandbox of Chromium (QTWEBENGINE_DISABLE_SANDBOX=1): its seccomp filter kills the process of the page (SIGSYS) with recent kernels and glibc, and Ubuntu 24.04 only gives user namespaces to the programs with an AppArmor profile; the window only shows the local page of the assistant. The job linux of installers.yml tests it on Ubuntu 22.04 and 24.04 (also on each push that changes installer/, environment.yml or the workflow): it installs it, opens the assistant in a virtual screen (Xvfb) and checks that the screenshot is not blank. Locally, with Docker and an ubuntu:24.04 image with wget, xvfb, x11-utils and imagemagick, as a user that is not root.

The version of Ollama that the assistant installs is OLLAMA in engine.py, with the size and the SHA-256 of the file of each system in ASSETS (the digest of each file in https://api.github.com/repos/ollama/ollama/releases/tags/<version>). Whoever already has another version installed keeps using it (Engine.installed); the new one is downloaded when there is none.

QGIS plugin

Build the zip by hand: python qgis/build_plugin.py (it downloads the dependencies, so it needs internet; the result is in dist/). The zip contains monitoreo and the Earth Engine library with its dependencies (pure Python, valid on Windows, macOS and Linux). With bandit and detect-secrets installed, it analyzes it like plugins.qgis.org and fails if the plugin or monitoreo have any finding.

Test it without a window with the Python of QGIS (it computes in Earth Engine):

PYTHONNOUSERSITE=1 EE_PROJECT=my-project /Applications/QGIS-final-4_0_3.app/Contents/MacOS/python qgis/test_plugin.py

Publish a version:

  1. Raise version= in qgis/monitoreo_qgis/metadata.txt, commit and push.
  2. Publish a release on GitHub (Releases > Draft a new release, new tag with that version, e.g. v0.3.7, Publish release). It is also valid to push the tag from GitHub Desktop (History, right-click on the commit, Create Tag…, Push origin): the workflow creates the release.
  3. A few minutes later, the workflow .github/workflows/qgis-plugin.yml (Actions tab) builds the zip on GitHub, attaches it to the release and, if the repository has the secret QGIS_PLUGINS_TOKEN, uploads it to plugins.qgis.org, where it goes through the security analysis and the approval before being published. It fails if the tag does not match version= (0.2 and v0.2.0 are valid for 0.2.0).

To attach the zip to a release that already exists: Actions > QGIS plugin > Run workflow, with its tag.

Python and R packages

The version is in pyproject.toml, src/monitoreo/__init__.py and r/monitoreo/DESCRIPTION. They are installed from GitHub (branch main or a tag), so it is enough to push the change; the MCP server updates itself to the latest version of GitHub every time it starts (MCP server).

Shared account

Without a project (init() without EE_PROJECT, init(shared=True), the project field empty in QGIS and in the assistant, mo_init("") in R), the tools compute with the service account monitoreo-public of the project ee-ugr-default-project. Its token, valid for one hour and only for Earth Engine, is given by the broker in broker/ (main.py, only with the Python standard library), deployed in Cloud Run at https://monitoreo-broker-872770286973.europe-west1.run.app (GET /token; its address is in services.BROKER, and MONITOREO_BROKER changes it). The package asks for another token when it expires and does not allow exporting to Google Drive with this account.

There is no key that can leak: the service runs as monitoreo-broker, which can only create tokens of monitoreo-public, and this one has the role monitoreoCompute, which only computes (values, pixels, maps, thumbnails and tables of the public Earth Engine catalog): it does not read the assets of the project nor its tasks, nor export. That is why the role earthengine.viewer is not used, as it would let anyone with a token read the assets of the project. The container is built by monitoreo-build (role run.builder). Each address can ask for 60 tokens per hour; the Earth Engine quota is that of the project, shared with the app.

Each user of the shared account has their research EECUs: USER_LIMIT EECU-h per month (10 by default), so that nobody spends the quota of the project, which belongs to everyone. The user is the anonymous identifier of each installation (services.user_id(): mo- and 16 random hexadecimal digits, in ~/.config/monitoreo/user_id), which the package sends when asking for the token (/token?user=) and sets as the Earth Engine workload tag of everything it computes. The broker (monitoreo-broker, with the Monitoring Viewer role of the project) reads the metric «Completed EECU-seconds» of Cloud Monitoring grouped by workload tag since the 1st of the month (one query for everybody, every 5 minutes) and gives no tokens to whoever has spent theirs until the next month (403 with code: spent; the token they already have lasts one hour more at most). GET /usage?user= tells what the user and the project have spent (EECU_LIMIT, the quota of the tier of the project, 150 by default). Without an identifier (versions before 0.3.21) there is no token, except with REQUIRE_USER=0; if Cloud Monitoring does not answer, tokens are given. They are changed when deploying: USER_LIMIT=20 broker/deploy.sh ee-ugr-default-project. The limit relies on good faith: whoever deletes the identifier file starts with a new one (the switch below is still there). With their own account, the tools read the metric of their project directly (services.eecu_usage).

  • Deploy or update: broker/deploy.sh ee-ugr-default-project with gcloud logged in with an account that owns the project (everest@go.ugr.es). It can be repeated: it creates what is missing and deploys a new revision.
  • Stop it at once (the tokens already given stop being valid): gcloud iam service-accounts disable monitoreo-public@ee-ugr-default-project.iam.gserviceaccount.com --project ee-ugr-default-project, and enable to open it again.
  • Usage: the Cloud Run logs (monitoreo-broker, region europe-west1) and the Earth Engine quota of the project in the Google Cloud console.

Earth Engine app and results viewer

  • The app is generated with gee/build.py and published from the Code Editor: see Earth Engine app.
  • The viewer is a static folder written by monitoreo publish: see Results viewer.

License

Copyright (C) 2026 OBSNEV, Universidad de Granada.

GNU General Public License v3.0 or later (GPL-3.0-or-later; full text in LICENSE): it can be used, copied, modified and redistributed, also for commercial purposes, keeping the authorship notices (MonitorEO, OBSNEV, Universidad de Granada) and distributing the modified versions with the same license and their source code. It is the license of the QGIS plugins (QGIS is GPL v2 or later) and it admits the Apache 2.0 licensed libraries that the plugin carries (earthengine-api and Google's). The data and maps of the results viewer have their own license (CC BY 4.0 by default).

Clone this wiki locally