87 actinia
# %pip install -U leafmap
The cloud based geoprocessing platform actinia is able to ingest and analyse large volumes of geodata in the cloud.
For the following actinia example we use the actinia-python-client (source code) to establish the connection to an actinia instance. First install the actinia-python-client (for latest version, see actinia-python-client releases).
import os
import leafmap
%pip install actinia_python_client
Collecting actinia_python_client
Downloading actinia_python_client-0.4.1-py3-none-any.whl.metadata (1.5 kB) Requirement already satisfied: jinja2 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from actinia_python_client) (3.1.3) Requirement already satisfied: requests in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from actinia_python_client) (2.31.0)
Collecting sphinx-material (from actinia_python_client) Downloading sphinx_material-0.0.36-py3-none-any.whl.metadata (4.3 kB) Requirement already satisfied: MarkupSafe>=2.0 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from jinja2->actinia_python_client) (2.1.5) Requirement already satisfied: charset-normalizer<4,>=2 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from requests->actinia_python_client) (3.3.2) Requirement already satisfied: idna<4,>=2.5 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from requests->actinia_python_client) (3.7) Requirement already satisfied: urllib3<3,>=1.21.1 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from requests->actinia_python_client) (2.2.1) Requirement already satisfied: certifi>=2017.4.17 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from requests->actinia_python_client) (2024.2.2) Requirement already satisfied: sphinx>=2.0 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx-material->actinia_python_client) (7.3.7) Requirement already satisfied: beautifulsoup4 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx-material->actinia_python_client) (4.12.3)
Collecting python-slugify[unidecode] (from sphinx-material->actinia_python_client) Downloading python_slugify-8.0.4-py2.py3-none-any.whl.metadata (8.5 kB) Collecting css-html-js-minify (from sphinx-material->actinia_python_client)
Downloading css_html_js_minify-2.5.5-py2.py3-none-any.whl.metadata (12 kB) Requirement already satisfied: lxml in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx-material->actinia_python_client) (5.2.1) Requirement already satisfied: sphinxcontrib-applehelp in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (1.0.8) Requirement already satisfied: sphinxcontrib-devhelp in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (1.0.6) Requirement already satisfied: sphinxcontrib-jsmath in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (1.0.1) Requirement already satisfied: sphinxcontrib-htmlhelp>=2.0.0 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (2.0.5) Requirement already satisfied: sphinxcontrib-serializinghtml>=1.1.9 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (1.1.10) Requirement already satisfied: sphinxcontrib-qthelp in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (1.0.7) Requirement already satisfied: Pygments>=2.14 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (2.17.2) Requirement already satisfied: docutils<0.22,>=0.18.1 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (0.21.1) Requirement already satisfied: snowballstemmer>=2.0 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (2.2.0) Requirement already satisfied: babel>=2.9 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (2.14.0) Requirement already satisfied: alabaster~=0.7.14 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (0.7.16) Requirement already satisfied: imagesize>=1.3 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (1.4.1) Requirement already satisfied: packaging>=21.0 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from sphinx>=2.0->sphinx-material->actinia_python_client) (24.0) Requirement already satisfied: soupsieve>1.2 in /home/runner/miniconda3/envs/test/lib/python3.11/site-packages (from beautifulsoup4->sphinx-material->actinia_python_client) (2.5)
Collecting text-unidecode>=1.3 (from python-slugify[unidecode]->sphinx-material->actinia_python_client) Downloading text_unidecode-1.3-py2.py3-none-any.whl.metadata (2.4 kB)
Collecting Unidecode>=1.1.1 (from python-slugify[unidecode]->sphinx-material->actinia_python_client) Downloading Unidecode-1.3.8-py3-none-any.whl.metadata (13 kB)
Downloading actinia_python_client-0.4.1-py3-none-any.whl (33 kB) Downloading sphinx_material-0.0.36-py3-none-any.whl (790 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/790.4 kB ? eta -:--:-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 790.4/790.4 kB 26.1 MB/s eta 0:00:00 Downloading css_html_js_minify-2.5.5-py2.py3-none-any.whl (40 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/40.5 kB ? eta -:--:-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.5/40.5 kB 14.8 MB/s eta 0:00:00
Downloading text_unidecode-1.3-py2.py3-none-any.whl (78 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/78.2 kB ? eta -:--:-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.2/78.2 kB 28.0 MB/s eta 0:00:00 Downloading Unidecode-1.3.8-py3-none-any.whl (235 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/235.5 kB ? eta -:--:-- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 235.5/235.5 kB 62.9 MB/s eta 0:00:00 Downloading python_slugify-8.0.4-py2.py3-none-any.whl (10 kB)
Installing collected packages: text-unidecode, Unidecode, python-slugify, css-html-js-minify, sphinx-material, actinia_python_client
Successfully installed Unidecode-1.3.8 actinia_python_client-0.4.1 css-html-js-minify-2.5.5 python-slugify-8.0.4 sphinx-material-0.0.36 text-unidecode-1.3
Note: you may need to restart the kernel to use updated packages.
The results of actinia ephemeral processing are available via object storage as GeoTIFF/COG or GeoPackage files.
Add a helper function for "pretty printing" of actinia results:
from json import dumps as json_dumps
def print_dict(input_dict, text=None):
if text:
print(text)
if "region" in input_dict:
input_dict["region"] = input_dict["region"].__dict__
print(json_dumps(input_dict, sort_keys=True, indent=4))
def print_dict_keys(input_dict, text=None):
if text:
print(text)
print(", ".join(input_dict.keys()))
Connect to the default actinia server which is defined in the actinia-python-client, currently https://actinia.mundialis.de.
# connect to the actinia server
from actinia import Actinia
# connect to default actinia server (https://actinia.mundialis.de)
actinia_mundialis = Actinia()
# retrieve metadata about actinia server and related software versions
version = actinia_mundialis.get_version()
print_dict(version, "Version is:")
Version is: { "api_version": "3.4.0", "grass_version": { "build_date": "2024-02-22", "build_off_t_size": "8", "build_platform": "x86_64-pc-linux-musl", "date": "2024", "gdal": "3.6.4", "geos": "3.11.2", "libgis_date": "2024-02-22T19:48:21+00:00", "libgis_revision": "8.3.2dev", "proj": "9.2.1", "revision": "6a52add", "sqlite": "3.41.2", "version": "8.3.2dev" }, "plugin_versions": { "actinia_metadata_plugin": "1.0.2", "actinia_module_plugin": "2.5.0", "actinia_satellite_plugin": "0.1.0", "actinia_stac_plugin": "0.1.1", "actinia_statistic_plugin": "0.2.1" }, "plugins": "actinia_statistic_plugin,actinia_satellite_plugin,actinia_metadata_plugin,actinia_module_plugin,actinia_stac_plugin", "python_version": "3.11.6 (main, Oct 4 2023, 06:22:18) [GCC 12.2.1 20220924]", "running_since": "n/a", "version": "4.13.1" }
Set the authentication settings of the actinia demo user to gain access to the actinia server functionality.
actinia_user = "demouser"
actinia_password = "gu3st!pa55w0rd"
# we use the default actinia server
actinia_mundialis.set_authentication(actinia_user, actinia_password)
print("Connected to actinia server.")
Connected to actinia server.
Obtain the list of locations and retrieve the metadata of a selected location.
# obtain the list of projects (called "locations") which are accessible to current user
locations = actinia_mundialis.get_locations()
print_dict_keys(locations, "Locations: ")
Locations: ECAD, nc_spm_08, latlong_wgs84, mylatlon
Retrieve the metadata of a selected location (this shows the respective projection information, spatial extent, resolution, etc.) to get an idea how the output looks like.
print_dict(actinia_mundialis.locations["nc_spm_08"].get_info(), "Location info:")
Location info: { "projection": "PROJCRS[\"NAD83(HARN) / North Carolina\",BASEGEOGCRS[\"NAD83(HARN)\",DATUM[\"NAD83 (High Accuracy Reference Network)\",ELLIPSOID[\"GRS 1980\",6378137,298.257222101,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],ID[\"EPSG\",4152]],CONVERSION[\"SPCS83 North Carolina zone (meters)\",METHOD[\"Lambert Conic Conformal (2SP)\",ID[\"EPSG\",9802]],PARAMETER[\"Latitude of false origin\",33.75,ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8821]],PARAMETER[\"Longitude of false origin\",-79,ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8822]],PARAMETER[\"Latitude of 1st standard parallel\",36.1666666666667,ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8823]],PARAMETER[\"Latitude of 2nd standard parallel\",34.3333333333333,ANGLEUNIT[\"degree\",0.0174532925199433],ID[\"EPSG\",8824]],PARAMETER[\"Easting at false origin\",609601.22,LENGTHUNIT[\"metre\",1],ID[\"EPSG\",8826]],PARAMETER[\"Northing at false origin\",0,LENGTHUNIT[\"metre\",1],ID[\"EPSG\",8827]]],CS[Cartesian,2],AXIS[\"easting (X)\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing (Y)\",north,ORDER[2],LENGTHUNIT[\"metre\",1]],USAGE[SCOPE[\"Engineering survey, topographic mapping.\"],AREA[\"United States (USA) - North Carolina - counties of Alamance; Alexander; Alleghany; Anson; Ashe; Avery; Beaufort; Bertie; Bladen; Brunswick; Buncombe; Burke; Cabarrus; Caldwell; Camden; Carteret; Caswell; Catawba; Chatham; Cherokee; Chowan; Clay; Cleveland; Columbus; Craven; Cumberland; Currituck; Dare; Davidson; Davie; Duplin; Durham; Edgecombe; Forsyth; Franklin; Gaston; Gates; Graham; Granville; Greene; Guilford; Halifax; Harnett; Haywood; Henderson; Hertford; Hoke; Hyde; Iredell; Jackson; Johnston; Jones; Lee; Lenoir; Lincoln; Macon; Madison; Martin; McDowell; Mecklenburg; Mitchell; Montgomery; Moore; Nash; New Hanover; Northampton; Onslow; Orange; Pamlico; Pasquotank; Pender; Perquimans; Person; Pitt; Polk; Randolph; Richmond; Robeson; Rockingham; Rowan; Rutherford; Sampson; Scotland; Stanly; Stokes; Surry; Swain; Transylvania; Tyrrell; Union; Vance; Wake; Warren; Washington; Watauga; Wayne; Wilkes; Wilson; Yadkin; Yancey.\"],BBOX[33.83,-84.33,36.59,-75.38]],ID[\"EPSG\",3358]]\n", "region": { "b": 0.0, "cells": 29535, "cells3": 29535, "cols": 179, "cols3": 179, "depths": 1, "e": 639530.0, "ewres": 10.0, "ewres3": 10.0, "n": 221230.0, "nsres": 10.0, "nsres3": 10.0, "projection": 99, "rows": 165, "rows3": 165, "s": 219580.0, "t": 1.0, "tbres": 1.0, "w": 637740.0, "zone": 0 } }
At this point the connection to the selected actinia server is properly established.
Reading the online data resource into the actinia server
Next we demonstrate the data processing of a raster map available online in actinia, here a sample DEM GeoTIFF file.
# define raster elevation map name
raster_layer_name = "srtm90"
# cache file locally
out_dir = os.getcwd()
dem_file = os.path.join(out_dir, f"{raster_layer_name}.tif")
# dem_url = (
# "https://drive.google.com/file/d/1vRkAWQYsLWCi6vcTMk8vLxoXMFbdMFn8/view?usp=sharing"
# )
dem_url = f"https://github.com/giswqs/data/raw/main/raster/{raster_layer_name}.tif"
# leafmap.download_file(dem_url, dem_file, unzip=False, overwrite=True)
Prepare actinia location and mapset, i.e. generate a subproject for data processing.
# request list of all locations
locations = actinia_mundialis.get_locations()
print([loc for loc in locations])
['ECAD', 'nc_spm_08', 'latlong_wgs84', 'mylatlon']
# remove leftover location from previous run
# actinia_mundialis.locations["latlong_wgs84"].delete()
#
# remove leftover mapset from previous run
locations["latlong_wgs84"].delete_mapset("elevation")
{'PERMANENT': <actinia.mapset.Mapset at 0x7f24c1f3c6d0>, 'Sentinel2A': <actinia.mapset.Mapset at 0x7f24c1f3f810>, 'globcover': <actinia.mapset.Mapset at 0x7f24c1f3d890>, 'modis_ndvi_global': <actinia.mapset.Mapset at 0x7f24c1f41750>}
# Create a new location for the data processing in actinia
new_location = actinia_mundialis.create_location("latlong_wgs84", 4326)
print(new_location.name)
print(new_location.region)
print([loc for loc in actinia_mundialis.locations])
# request list of mapsets in selected location
mapsets = actinia_mundialis.locations["latlong_wgs84"].get_mapsets()
print_dict_keys(mapsets, "Mapsets in latlong_wgs84:")
Location <latlong_wgs84> already exists.
latlong_wgs84 None ['ECAD', 'nc_spm_08', 'latlong_wgs84', 'mylatlon'] Mapsets in latlong_wgs84: PERMANENT, Sentinel2A, globcover, modis_ndvi_global
# Create a new mapset for the data processing in actinia
mapset_name = "elevation"
locations["latlong_wgs84"].create_mapset(mapset_name)
<actinia.mapset.Mapset at 0x7f24e408d310>
## Optional: Upload the sample DEM data set to actinia (indeed not needed since we use `vsicurl/` below
## to directly retrieve the online dataset).
# locations["latlong_wgs84"].mapsets[mapset_name].upload_raster(raster_layer_name, dem_file)
# print_dict_keys(locations["latlong_wgs84"].mapsets[mapset_name].raster_layers, "Raster maps in new mapset:")
Ephemeral Processing with actinia¶
Ephemeral processing is used to keep computed results, including user-generated data and temporary data, only for a limited period of time (e.g. 24 hours by default in the actinia demo server). This reduces cloud storage costs.
In contrast, persistent processing refers to the persistent retention of data without a scheduled deletion time, even in the event of a power outage, resulting in corresponding storage costs. In the geo/EO context, persistent storage is used to provide, for example, basic cartography, i.e. elevation models, road networks, building footprints, etc.
Hillshading example
Here an example for an ephemeral processing job: We download and import the remotely available GeoTIFF file. Then we use r.relief to generate a hillshading map and pre-define the resolution to 10 m. The computational region is set to the input elevation map. The resulting hillshade.tif
raster map is then provided as a resource for download and visualization.
pc = {
"list": [
{
"id": "importer_0",
"comment": "Import of remote data source (here: COG)",
"module": "r.import",
"inputs": [
{"param": "input", "value": f"/vsicurl/{dem_url}"},
{"param": "memory", "value": "2000"},
{"param": "extent", "value": "input"},
],
"outputs": [{"param": "output", "value": f"{raster_layer_name}"}],
},
{
"id": "r.info_1",
"comment": "Print metadata of imported raster map",
"module": "r.info",
"inputs": [{"param": "map", "value": f"{raster_layer_name}"}],
},
{
"id": "computational_region_2",
"comment": "Set computational region to imported map, and print settings",
"module": "g.region",
"inputs": [{"param": "raster", "value": f"{raster_layer_name}"}],
"stdout": {"id": "region", "format": "kv", "delimiter": "="},
"flags": "g",
},
{
"id": "create_hillshading_3",
"comment": "Compute hillshading map",
"module": "r.relief",
"inputs": [{"param": "input", "value": f"{raster_layer_name}"}],
"outputs": [{"param": "output", "value": "hillshade"}],
},
{
"id": "exporter_4",
"comment": "Export hillshading map to COG file",
"module": "exporter",
"outputs": [
{
"export": {"type": "raster", "format": "COG"},
"param": "map",
"value": "hillshade",
}
],
},
],
"version": "1",
}
print(pc)
job = actinia_mundialis.locations["latlong_wgs84"].create_processing_export_job(
pc, "hillshading"
)
job.poll_until_finished()
print(job.status)
print(job.message)
exported_raster = job.urls["resources"][0]
print(exported_raster)
{'list': [{'id': 'importer_0', 'comment': 'Import of remote data source (here: COG)', 'module': 'r.import', 'inputs': [{'param': 'input', 'value': '/vsicurl/https://github.com/giswqs/data/raw/main/raster/srtm90.tif'}, {'param': 'memory', 'value': '2000'}, {'param': 'extent', 'value': 'input'}], 'outputs': [{'param': 'output', 'value': 'srtm90'}]}, {'id': 'r.info_1', 'comment': 'Print metadata of imported raster map', 'module': 'r.info', 'inputs': [{'param': 'map', 'value': 'srtm90'}]}, {'id': 'computational_region_2', 'comment': 'Set computational region to imported map, and print settings', 'module': 'g.region', 'inputs': [{'param': 'raster', 'value': 'srtm90'}], 'stdout': {'id': 'region', 'format': 'kv', 'delimiter': '='}, 'flags': 'g'}, {'id': 'create_hillshading_3', 'comment': 'Compute hillshading map', 'module': 'r.relief', 'inputs': [{'param': 'input', 'value': 'srtm90'}], 'outputs': [{'param': 'output', 'value': 'hillshade'}]}, {'id': 'exporter_4', 'comment': 'Export hillshading map to COG file', 'module': 'exporter', 'outputs': [{'export': {'type': 'raster', 'format': 'COG'}, 'param': 'map', 'value': 'hillshade'}]}], 'version': '1'}
Status of hillshading job is accepted: Resource accepted
Status of hillshading job is running: Moving generated resources to final destination
Status of hillshading job is finished: Processing successfully finished
finished Processing successfully finished https://actinia.mundialis.de/api/v3/resources/demouser/resource_id-9fb2c5d7-9dcd-45e7-815a-d518c68f4ee3/hillshade.tif
It will take a moment, then the communication by actinia is shown: "Status of hillshading job is accepted: Resource accepted" continued by further communication messages.
In case an error occurs, check the process log (use [x] with x being the step number in the process chain). Examples:
# check step 0 (r.import)
print_dict(job.process_log[0])
{ "executable": "r.import", "id": "importer_0", "mapset_size": 16011901, "parameter": [ "input=/vsicurl/https://github.com/giswqs/data/raw/main/raster/srtm90.tif", "memory=2000", "extent=input", "output=srtm90" ], "return_code": 0, "run_time": 2.3834228515625, "stderr": [ "Importing raster map <srtm90>...", "0..3..6..9..12..15..18..21..24..27..30..33..36..39..42..45..48..51..54..57..60..63..66..69..72..75..78..81..84..87..90..93..96..99..100", "" ], "stdout": "" }
# check step 2 (g.region)
print_dict(job.process_log[2])
{ "executable": "g.region", "id": "computational_region_2", "mapset_size": 16011946, "parameter": [ "raster=srtm90", "-g" ], "return_code": 0, "run_time": 0.17988300323486328, "stderr": [ "" ], "stdout": "projection=3\nzone=0\nn=38.6269166666667\ns=36.6412222222222\nw=-120.743426862778\ne=-117.304510196111\nnsres=0.000805555555555557\newres=0.000805555555555557\nrows=2465\ncols=4269\ncells=10523085\n" }
# check step 3 (r.relief)
print_dict(job.process_log[3])
{ "executable": "r.relief", "id": "create_hillshading_3", "mapset_size": 29121905, "parameter": [ "input=srtm90", "output=hillshade" ], "return_code": 0, "run_time": 2.64412522315979, "stderr": [ "2..5..8..11..14..17..20..23..26..29..32..35..38..41..44..47..50..53..56..59..62..65..68..71..74..77..80..83..86..89..92..95..98..100", "Shaded relief raster map <hillshade> complete", "" ], "stdout": "" }
Inject user:password@server
into exported_raster
URL (i.e., the actinia resource).
url = exported_raster.replace("//", f"//{actinia_user}:{actinia_password}@")
print(url)
https://demouser:gu3st!pa55w0rd@actinia.mundialis.de/api/v3/resources/demouser/resource_id-9fb2c5d7-9dcd-45e7-815a-d518c68f4ee3/hillshade.tif
Visualize the hillshade
map in leafmap (colorbar inspired by this notebook):
m = leafmap.Map()
m.add_basemap("OpenTopoMap")
m.add_colormap(
cmap="terrain",
label="Elevation",
width=3,
height=0.3,
orientation="horizontal",
vmin=0,
vmax=4000,
)
m.add_cog_layer(
url,
name="SRTM90 hillshaded map",
attribution='<a href="https://e4ftl01.cr.usgs.gov/MEASURES/">https://e4ftl01.cr.usgs.gov/MEASURES/</a>',
)
# show map
m
Find further leafmap (styling) tools in the upper-right toolbox of leafmap.