Interagir programmatiquement avec GeoServer en Python

Les 3 moyens pour configurer GeoServer

Pour rappel, la configuration de GeoServer (l’ajout de données, le paramétrage des services, la sécurité, le logging, etc) peut se faire via 3 canaux différents :

  • l’interface Web d’administration : accessible via un navigateur web sur http://localhost:8080/geoserver/web dans une installation par défaut de GeoServer
  • les fichiers de configuration : ce qu’on appelle le « data directory », ou encore le « GEOSERVER_DATA_DIR » (du nom de la variable d’environnement pointant vers le répertoire en question)
  • l’interface HTTP REST : accessible sur http://localhost:8080/geoserver/rest, où chaque ressource est représentée par une URL sur laquelle les opérations GET (pour lire) et PUT, POST, DELETE (pour modifier) sont disponibles

Depuis l’environnement Python, le moyen le plus évident d’interagir et de configurer GeoServer est très certainement l’interface HTTP REST, même si à priori tout ce qui peut être fait via l’interface Web d’administration n’est pas forcément disponible via les requêtes HTTP (ex : lancer le recalcul de la bounding box sur base des données pour une couche donnée)

Python et GeoServer concrètement

Nous voilà donc prêts à interagir programmatiquement avec GeoServer en Python, via son API HTTP REST. 

Pour ce faire, 2 solutions s’offrent à nous (complémentaire selon moi) :

  1. La librairie Open-Source « gsconfig« , disponible sur PyPI (https://pypi.org/project/gsconfig/) et distribuée sous licence MIT
  2. La librairie Open-Source « requests« , également disponible sur PyPI (https://pypi.org/project/requests/), distribuée sous licence Apache2

Ces librairies peuvent très facilement être installée dans votre environnement virtuel via l’utilitaire « pip ».

Petit rappel en passant, au sujet des licences : un grand nombre de projets Open-Source que l’on trouve aujourd’hui sont sous licence GPL. Une librairie publié sous GPL ne peut cependant être utilisée dans un produit commercial sans que le produit ne devienne lui-même également Open-Source. Les licences MIT, BSD, ISC et Apache2 sont d’excellentes alternatives à la GPL qui permettent à une librairie Open-Source d’être utilisé librement dans un logiciel propriétaire et fermé…

La librairie « gsconfig »

gsconfig permet donc d’interagir en Python avec GeoServer en manipulant des objets de haut-niveau, ce qui est assez élégant : on dispose de classes « Catalog », « Layer », « Style, « Resource », etc…

Voici un exemple d’utilisation de la librairie :

from geoserver.catalog import Catalog

[...]

# Connect to Catalog, with REST URL, user and password
cat = Catalog("http://localhost:8080/geoserver/rest/","admin","geoserver")

# Get the workspace "myworkspace"
workspace = cat.get_workspace("myworkspace)

# Get the store (ie CoverageStore) "Mystore"
store = cat.get_store("mystore", "myworkspace")

# Get, update and save a resource
resource = cat.get_resource("mylayer", workspace="myworkspace")
resource.title = "That's my new title"
catalog.save(resource)

# Add styles (default and available styles) on a layer
layer = cat.get_layer("mylayer")
raster_tc_style = cat.get_style("raster_tc")
raster_fc_style = cat.get_style("raster_fc")
layer.default_style = raster_tc_style
layer.styles=[raster_tc_style, raster_fc_style]
cat.save(layer)

[...]

Quelques remarques cependant :

  • Le statut de développement de cette librairie sur PyPI est « 4 – Beta », et donc cette librairie n’est pas encore disponible en version « Stable »
  • La librairie ne semble pas non plus être activement maintenue, puisque la dernière version disponible sur PyPI (la 1.0.10) date de juillet 2018
  • Peu d’exemple d’utilisation sont disponibles sur le web…
  • Certaines fonctionnalités semblent ne pas … fonctionner. J’ai par exemple eu quelques problèmes lors de l’utilisation de gsconfig pour configurer des ressources de type ImagePyramid (extension de GeoServer). Je ferai certainement un article dédié à ce problème précis.

D’où le besoin d’utiliser en complément la librairie « requests », là où c’est nécessaire…

L’utilisation de « requests »

Là où nous nous heurtons aux limitations de « gsconfig », nous avons la possibilité de revenir à la librairie « requests » pour réaliser nos opérations.

Bien évidemment, la librairie « requests » ne permet pas de manipuler des concepts de haut-niveau GeoServer (plus de « Catalog », « Layer », etc)

Voici un exemple d’appel réalisé avec « requests », afin de créer un Coverage lié au CoverageStore de type « ImagePyramid » (qui lui peut être crée via « gsconfig »)

import requests

[...]

# Create a coverage from the coverage store
coverages_url = "http://localhost:8080/geoserver/rest/workspaces/myworkspace/coveragestores/mystore/coverages"

payload = """<coverage>
<name>mycoverage</name><srs>EPSG:31370</srs>
<dimensions>
<coverageDimension><name>RED_BAND</name><description>GridSampleDimension[-Infinity,Infinity]</description><range><min>-inf</min><max>inf</max></range><nullValues><double>0.0</double></nullValues><dimensionType><name>UNSIGNED_8BITS</name></dimensionType></coverageDimension>
<coverageDimension><name>GREEN_BAND</name><description>GridSampleDimension[-Infinity,Infinity]</description><range><min>-inf</min><max>inf</max></range><nullValues><double>0.0</double></nullValues><dimensionType><name>UNSIGNED_8BITS</name></dimensionType></coverageDimension>
<coverageDimension><name>BLUE_BAND</name><description>GridSampleDimension[-Infinity,Infinity]</description><range><min>-inf</min><max>inf</max></range><nullValues><double>0.0</double></nullValues><dimensionType><name>UNSIGNED_8BITS</name></dimensionType></coverageDimension>
<coverageDimension><name>NIR_BAND</name><description>GridSampleDimension[-Infinity,Infinity]</description><range><min>-inf</min><max>inf</max></range><nullValues><double>0.0</double></nullValues><dimensionType><name>UNSIGNED_8BITS</name></dimensionType></coverageDimension>
<coverageDimension><name>ALPHA_BAND</name><description>GridSampleDimension[-Infinity,Infinity]</description><range><min>-inf</min><max>inf</max></range><nullValues><double>0.0</double></nullValues><dimensionType><name>UNSIGNED_8BITS</name></dimensionType></coverageDimension>
</dimensions>
</coverage>"""

headers = {
	  "Content-type": "text/xml"
	}
	
post_response = requests.post(coverages_url, data = payload, headers = headers, auth = ("admin", "geoserver"))

# Check that status code is 201 - Created"
assert (post_response.status_code == 201)

[...]

C’est du bas-niveau, moins parlant et moins lisible qu’avec « gsconfig », mais cela fonctionne !

A noter que cette méthode peut tout à fait être utilisée pour interagir programmatiquement avec GeoWebCache (fera également l’objet d’un article séparé)

Conclusion

L’utilisation combinée des librairies « gsconfig » et de « requests » nous permet tout à fait d’interagir programmatiquement avec GeoServer, au travers de son API HTTP REST.

Je n’ai finalement jamais été totalement bloqué dans la création de mes jeux de données et la configuration des ressources, mais il a fallu passer de l’une à l’autre méthode. Y compris lors de mes tests avec des extensions de GeoServer, comme « ImagePyramid » par exemple…

  •  
  •  
  •  
  •