Programmatically Interact with GeoServer in Python

The 3 Ways to Configure GeoServer

As a reminder, the configuration of GeoServer (adding data, configuring services, security, logging, etc.) can be done via 3 different channels:

  • the Web administration interface: accessed through a web browser at http://localhost:8080/geoserver/web in a default GeoServer installation
  • the configuration files: what we call “data directory”, aka “GEOSERVER_DATA_DIR” (name of the environment variable pointing to that directory)
  • the HTTP REST interface: at http://localhost:8080/geoserver/rest, where each resource is represented by a URL on which the operations GET (to read) and PUT, POST, DELETE (to modify) are available

From the Python environment, the most obvious way to interact and configure GeoServer is most certainly the HTTP REST interface, even if it looks like everything that can be done via the administrative Web interface is not necessarily available via HTTP requests (ex: trigger the recalculation of the bounding box based on the data for a given layer)

Python and GeoServer in Concrete Terms

So here we are ready to interact programmatically with GeoServer in Python, via its HTTP REST API.

To do this, 2 solutions are available to us (complementary in my opinion):

  1. The Open-Source “gsconfig” library , available on PyPI (https://pypi.org/project/gsconfig/) and distributed under the MIT Licence
  2. The Open-Source “requests” library, available on PyPI as well (https://pypi.org/project/requests/), and distributed under the Apache2 Licence

These libraries can very easily be installed in your virtual environment using the “pip” utility.

Just a quick reminder so far, about licenses: a large number of Open-Source projects that we find today are under GPL license. However, a library published under GPL cannot be used in a commercial product without the product itself also becomes Open-Source. The MIT, BSD, ISC and Apache2 licenses are excellent alternatives to the GPL which allow an Open-Source library to be used freely in proprietary and non open-source software …

The “gsconfig” Library

gsconfig therefore allows you to interact in Python with GeoServer by manipulating high-level objects, which is quite elegant: we have classes “Catalog”, “Layer”, “Style,” Resource “, etc …

Here is an example of use:

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)

[...]

Some remarks:

  • The development status of this library on PyPI is “4 – Beta”, and therefore this library is not yet available in “Stable” version
  • The library does not seem to be actively maintained, since the last version available on PyPI (1.0.10) dates from July 2018
  • Few usage examples are available on the web …
  • Some features don’t seem to … work. For example, I had some problems when using gsconfig to configure ImagePyramid type resources (extension of GeoServer). I will certainly write an article dedicated to this specific problem.

Hence the need to use the “requests” library in addition, where necessary …

The Use of “requests”

Where we encounter the limitations of “gsconfig”, we have the option to use the “requests” library to carry out our operations.

Obviously, the “requests” library does not allow us to manipulate high-level GeoServer concepts (no more “Catalog”, “Layer”, etc.)

Here is an example of a call made with “requests”, in order to create a Coverage linked to the CoverageStore of type “ImagePyramid” (which can be created 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)

[...]

It’s low-level, less meaningful and less readable than with “gsconfig”, but it works!

Note that this method can also be used to interact programmatically with GeoWebCache (will also be the subject of a separate article)

Conclusion

The combined use of the “gsconfig” and “requests” libraries allows us to interact programmatically with GeoServer, through its HTTP REST API.

I was never totally blocked in the creation of my datasets and the configuration of resources, but I had to switch from one to the other method. Including during my tests with GeoServer extensions, like “ImagePyramid” for example …

  •  
  •  
  •  
  •