All In One¶
This notebook showcases some of the most common use cases to interact with a GeoServer
server using its REST API through the geoserver-py
Python package.
Imports¶
Let's start by importing the required libraries:
from pathlib import Path
from geoserver import GeoServer
Config¶
We'll set up the configuration for the notebook:
# Directory containing sample data
DATA_DIR = Path("../tests/data")
assert DATA_DIR.exists(), f"The directory {DATA_DIR} does not exist."
geoserver = GeoServer(
"http://localhost:8080/geoserver",
username="admin",
password="geoserver"
)
Setup¶
If the demo workspace already exists, we will delete it so that the demo can be run multiple times.
if geoserver.workspace_exists("demo"):
geoserver.delete_workspace("demo", recurse=True)
geoserver.get_workspaces()
{'workspaces': ''}
It is also possible to return the result as XML format using:
workspace = geoserver.get_workspaces(format="xml") # Default is "json"
print(workspace)
<workspaces/>
Create a new workspace¶
To create a new workspace, use the create_workspace
method. This method will return a success message if the workspace is created successfully.
# Using JSON format
body = {"workspace": {"name": "demo"}}
# Using XML format
body = "<workspace><name>demo</name></workspace>"
geoserver.create_workspace(body=body)
'Created'
Select a workspace¶
To select a specific workspace, use the get_workspace
method.
geoserver.get_workspace("demo")
{'workspace': {'name': 'demo', 'isolated': False, 'dateCreated': '2024-06-12 22:29:47.282 UTC', 'dataStores': 'http://localhost:8080/geoserver/rest/workspaces/demo/datastores.json', 'coverageStores': 'http://localhost:8080/geoserver/rest/workspaces/demo/coveragestores.json', 'wmsStores': 'http://localhost:8080/geoserver/rest/workspaces/demo/wmsstores.json', 'wmtsStores': 'http://localhost:8080/geoserver/rest/workspaces/demo/wmtsstores.json'}}
Update a workspace¶
To update a workspace, use the update_workspace
method.
# Using JSON format
body = {"workspace": {"name": "demo", "enabled": "true"}}
# Using XML format
body = "<workspace><name>demo</name><enabled>true</enabled></workspace>"
geoserver.update_workspace("demo", body=body)
'Updated'
Remove a workspace¶
To remove a workspace, use the delete_workspace
method. This method will return a success message if the workspace is removed successfully. If the workspace is not empty, an error will be raised. To remove a workspace and all its contents, use the recurse=True
argument.
geoserver.delete_workspace(workspace="demo", recurse=True)
Datastores¶
This section will guide you on how to interact with data stores in the GeoServer instance.
List existing data stores¶
To view the data stores in the workspace, use the get_data_stores
method:
geoserver.get_data_stores(workspace="demo")
{'dataStores': ''}
Create a new data stores¶
There are different ways to create / add data in a GeoServer instance. You can either upload diretly your data (from a local file, external URL, PostGIS, etc.) or create a new data store from a data already present in the GeoServer instance (e.g. you can reference a file that is already present in the data
directory of the geoserver).
From a local shapefile¶
To upload a data store from a local shapefile, use the upload_data_store
method. This method will return a success message if the data store is created successfully.
# Get the shapefile
file_path = DATA_DIR / "vectors" / "buildings.shp"
assert file_path.exists(), f"File not found: {file_path.as_posix()!r}"
# If the store already exists, it will be overwritten
geoserver.upload_data_store(file_path, name="buildings", workspace="demo")
# If the `store` parameter is not provided, the name of the store will be the same as the file name
geoserver.upload_data_store(file_path, workspace="demo")
'Created'
From a file already present in the GeoServer instance¶
Now that we have uploaded a file buildings.shp
to the GeoServer instance, we can create a new data store from this file.
# Using JSON format
body={
"dataStore": {
"name": "buildings_v2",
"connectionParameters": {
"entry": [
{"@key":"url", "$": "file:data/demo/buildings/buildings.shp"},
{"@key":"filetype", "$": "shapefile"},
]
}
}
}
# Using XML format
body = """
<dataStore>
<name>buildings_v2</name>
<connectionParameters>
<entry key="url">file:data/demo/buildings/buildings.shp</entry>
<entry key="filetype">shapefile</entry>
</connectionParameters>
</dataStore>
"""
geoserver.create_data_store(body=body, workspace="demo")
'Created'
From a PostGIS database¶
You can also add a PostGIS data store to the GeoServer instance. To do this, you need to provide the database connection details.
You will have to upload the data to the PostGIS database first:
!ogr2ogr -f PostgreSQL PG:"host=localhost port=5432 user=admin dbname=db password=postgres" ../tests/data/vectors/landuse.shp -nlt PROMOTE_TO_MULTI -lco OVERWRITE=YES
Warning 1: Layer creation options ignored since an existing layer is being appended to.
# Using JSON format
body = {
"dataStore": {
"name": "postgis",
"description": "PostGIS connection",
"connectionParameters": {
"host": "postgis",
"port": "5432",
"database": "db",
"user": "admin",
"passwd": "postgres",
"dbtype": "postgis",
"schema": "public",
"Expose primary keys": "true",
"Loose bbox": "true",
"Estimated extends": "true",
"fetch size": "1000",
"Max open prepared statements": "50",
"preparedStatements": "false",
"validate connections": "true",
"validate connections on borrow": "true",
"validate connections on return": "true",
"Connection timeout": "20",
"Eviction run periodicity": "3600",
"Min evictable idle time": "300",
"Max active": "50",
"Max idle": "10",
"Max wait": "10000",
"Test on borrow": "true",
"Test while idle": "true",
"Time between eviction runs": "60000",
}
}
}
# Using XML format
body = """
<dataStore>
<name>postgis</name>
<description>PostGIS connection</description>
<connectionParameters>
<entry key="host">postgis</entry>
<entry key="port">5432</entry>
<entry key="database">db</entry>
<entry key="user">admin</entry>
<entry key="passwd">postgres</entry>
<entry key="dbtype">postgis</entry>
<entry key="schema">public</entry>
<entry key="Expose primary keys">true</entry>
<entry key="Loose bbox">true</entry>
<entry key="Estimated extends">true</entry>
<entry key="fetch size">1000</entry>
<entry key="Max open prepared statements">50</entry>
<entry key="preparedStatements">false</entry>
<entry key="validate connections">true</entry>
<entry key="validate connections on borrow">true</entry>
<entry key="validate connections on return">true</entry>
<entry key="Connection timeout">20</entry>
<entry key="Eviction run periodicity">3600</entry>
<entry key="Min evictable idle time">300</entry>
<entry key="Max active">50</entry>
<entry key="Max idle">10</entry>
<entry key="Max wait">10000</entry>
<entry key="Test on borrow">true</entry>
<entry key="Test while idle">true</entry>
<entry key="Time between eviction runs">60000</entry>
</connectionParameters>
</dataStore>
"""
geoserver.create_data_store(body=body, workspace="demo")
'Created'
Once added, you should also publish the feature types contained in the data store. Use the create_feature_type
method to do this.
# Using JSON format
body = {
"featureType": {
"name": "landuse",
"title": "landuse",
"advertised": "true",
}
}
# Using XML format
body = """
<featureType>
<name>landuse</name>
<title>landuse</title>
<advertised>true</advertised>
</featureType>
"""
geoserver.create_feature_type(body=body, workspace="demo", store="postgis")
'Created'
Select a data store¶
Now that we created a data store from a vector source, we can list it using the get_data_stores
method.
geoserver.get_data_store(name="buildings", workspace="demo")
{'dataStore': {'name': 'buildings', 'type': 'Shapefile', 'enabled': True, 'workspace': {'name': 'demo', 'href': 'http://localhost:8080/geoserver/rest/workspaces/demo.json'}, 'connectionParameters': {'entry': [{'@key': 'namespace', '$': 'http://demo'}, {'@key': 'url', '$': 'file:/opt/geoserver/data_dir/data/demo/buildings/buildings.shp'}]}, '_default': False, 'dateCreated': '2024-06-12 22:29:47.368 UTC', 'dateModified': '2024-06-12 22:29:47.377 UTC', 'disableOnConnFailure': False, 'featureTypes': 'http://localhost:8080/geoserver/rest/workspaces/demo/datastores/buildings/featuretypes.json'}}
Clear data caches¶
You can clear the data caches using the clear_data_store_cache
method.
geoserver.reset_data_store_caches(name="buildings", workspace="demo")
'OK'
Update a data store¶
To update a data store, use the update_data_store
method.
# Using JSON format
body = {
"dataStore": {
"name": "buildings",
}
}
# Using XML format
body = "<dataStore><name>buildings</name></dataStore>"
geoserver.update_data_store(name="buildings", body=body, workspace="demo")
'Updated'
Remove a data store¶
To delete a datastore, use the following command:
geoserver.delete_data_store(name="buildings", workspace="demo")
Coverage Stores¶
Coverage stores are used to store raster data. A coverage is a raster based data which originates from a coverage store.
List existing coverage stores¶
To list the existing coverage stores, use the get_coverage_stores
method.
geoserver.get_coverage_stores(workspace="demo")
{'coverageStores': ''}
Create a new coverage store¶
You can create a coverage store from a local raster file. Use the upload_coverage_store
method to create a new coverage store.
file_path = DATA_DIR / "rasters" / "raster.tif"
assert file_path.exists(), f"File not found: {file_path.as_posix()!r}"
# If the store already exists, it will be overwritten
geoserver.upload_coverage_store(file_path, format="geotiff", workspace="demo", name="raster")
# If the `store` parameter is not provided, the name of the store will be the same as the file name
geoserver.upload_coverage_store(file_path, format="geotiff", workspace="demo")
'Created'
Select a coverage store¶
To select a specific coverage store, use the get_coverage_store
method.
geoserver.get_coverage_store(name="raster", workspace="demo")
{'coverageStore': {'name': 'raster', 'type': 'GeoTIFF', 'enabled': True, 'workspace': {'name': 'demo', 'href': 'http://localhost:8080/geoserver/rest/workspaces/demo.json'}, '_default': False, 'dateCreated': '2024-06-12 22:29:47.909 UTC', 'dateModified': '2024-06-12 22:29:47.928 UTC', 'disableOnConnFailure': False, 'url': 'file:data/demo/raster/raster.tif', 'coverages': 'http://localhost:8080/geoserver/rest/workspaces/demo/coveragestores/raster/coverages.json'}}
Update a coverage store¶
# Using JSON format
body = {
"coverageStore": {
"name": "raster",
}
}
# Using XML format
body = """
<coverageStore>
<name>raster</name>
</coverageStore>
"""
geoserver.update_coverage_store(name="raster", body=body, workspace="demo")
'Updated'
Remove a coverage store¶
To remove a coverage store, use the delete_coverage_store
method.
geoserver.delete_coverage_store(name="raster", workspace="demo", recurse=True)
geoserver.get_coverages(workspace="demo")
{'coverages': {'coverage': [{'name': 'raster', 'href': 'http://localhost:8080/geoserver/rest/workspaces/demo/coverages/raster.json'}]}}
Select a coverage¶
To select a specific coverage, use the get_coverage
method.
geoserver.get_coverage(workspace="demo", name="raster")
{'coverage': {'name': 'raster', 'nativeName': 'raster', 'namespace': {'name': 'demo', 'href': 'http://localhost:8080/geoserver/rest/namespaces/demo.json'}, 'title': 'raster', 'description': 'Generated from GeoTIFF', 'keywords': {'string': ['raster', 'WCS', 'GeoTIFF']}, 'nativeCRS': 'GEOGCS["WGS 84", \n DATUM["World Geodetic System 1984", \n SPHEROID["WGS 84", 6378137.0, 298.257223563, AUTHORITY["EPSG","7030"]], \n AUTHORITY["EPSG","6326"]], \n PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], \n UNIT["degree", 0.017453292519943295], \n AXIS["Geodetic longitude", EAST], \n AXIS["Geodetic latitude", NORTH], \n AUTHORITY["EPSG","4326"]]', 'srs': 'EPSG:4326', 'nativeBoundingBox': {'minx': -1.514093836, 'maxx': -1.508900084, 'miny': 48.63391919, 'maxy': 48.638064247, 'crs': 'EPSG:4326'}, 'latLonBoundingBox': {'minx': -1.514093836, 'maxx': -1.508900084, 'miny': 48.63391919, 'maxy': 48.638064247, 'crs': 'EPSG:4326'}, 'projectionPolicy': 'REPROJECT_TO_DECLARED', 'enabled': True, 'advertised': True, 'metadata': {'entry': {'@key': 'dirName', '$': 'raster_null'}}, 'store': {'@class': 'coverageStore', 'name': 'demo:raster', 'href': 'http://localhost:8080/geoserver/rest/workspaces/demo/coveragestores/raster.json'}, 'serviceConfiguration': False, 'simpleConversionEnabled': False, 'nativeFormat': 'GeoTIFF', 'grid': {'@dimension': 2, 'range': {'low': '0 0', 'high': '866 691'}, 'transform': {'scaleX': '5.997404157043939E-6', 'scaleY': '-5.998635311146656E-6', 'shearX': 0, 'shearY': 0, 'translateX': -1.5140908372979216, 'translateY': 48.638061247682344}, 'crs': 'EPSG:4326'}, 'supportedFormats': {'string': ['GIF', 'PNG', 'JPEG', 'TIFF', 'NetCDF', 'EHdr', 'VRT', 'GEOTIFF', 'DTED', 'ArcGrid', 'RST', 'GeoPackage (mosaic)', 'ImageMosaic', 'SRP', 'ENVIHdr', 'ERDASImg', 'ImagePyramid', 'AIG', 'RPFTOC', 'NITF']}, 'interpolationMethods': {'string': ['nearest neighbor', 'bilinear', 'bicubic']}, 'defaultInterpolationMethod': 'nearest neighbor', 'dimensions': {'coverageDimension': [{'name': 'RED_BAND', 'description': 'GridSampleDimension[-Infinity,Infinity]', 'range': {'min': '-inf', 'max': 'inf'}, 'unit': 'W.m-2.Sr-1', 'dimensionType': {'name': 'UNSIGNED_8BITS'}}, {'name': 'GREEN_BAND', 'description': 'GridSampleDimension[-Infinity,Infinity]', 'range': {'min': '-inf', 'max': 'inf'}, 'unit': 'W.m-2.Sr-1', 'dimensionType': {'name': 'UNSIGNED_8BITS'}}, {'name': 'BLUE_BAND', 'description': 'GridSampleDimension[-Infinity,Infinity]', 'range': {'min': '-inf', 'max': 'inf'}, 'unit': 'W.m-2.Sr-1', 'dimensionType': {'name': 'UNSIGNED_8BITS'}}, {'name': 'ALPHA_BAND', 'description': 'GridSampleDimension[-Infinity,Infinity]', 'range': {'min': '-inf', 'max': 'inf'}, 'dimensionType': {'name': 'UNSIGNED_8BITS'}}]}, 'requestSRS': {'string': 'EPSG:4326'}, 'responseSRS': {'string': 'EPSG:4326'}, 'parameters': {'entry': [{'string': ['InputTransparentColor', '']}, {'string': ['SUGGESTED_TILE_SIZE', '512,512']}, {'string': ['Bands', '']}, {'string': ['RescalePixels', True]}]}, 'nativeCoverageName': 'raster'}}
geoserver.get_styles()
{'styles': {'style': [{'name': 'generic', 'href': 'http://localhost:8080/geoserver/rest/styles/generic.json'}, {'name': 'line', 'href': 'http://localhost:8080/geoserver/rest/styles/line.json'}, {'name': 'point', 'href': 'http://localhost:8080/geoserver/rest/styles/point.json'}, {'name': 'polygon', 'href': 'http://localhost:8080/geoserver/rest/styles/polygon.json'}, {'name': 'raster', 'href': 'http://localhost:8080/geoserver/rest/styles/raster.json'}]}}
Retrieve a style¶
To get a specific style, use the get_style
method.
geoserver.get_style("point")
{'style': {'name': 'point', 'format': 'sld', 'languageVersion': {'version': '1.0.0'}, 'filename': 'default_point.sld'}}
Creating a style¶
You can create a new style on the server in two ways. In the first way, the creation is done in two steps: the style entry is created in the catalog, and then the style content is uploaded. The second way can add the style to the server in a single step by uploading a file containing the style content.
Create a new style in two steps¶
# Using XML format
body = """
<style>
<name>elevation</name>
<filename>elevation.sld</filename>
</style>
"""
geoserver.create_style(body=body, workspace="demo")
'Created'
# Get the styles associated to the raster layer
file_path = DATA_DIR / "styles" / "elevation.sld"
assert file_path.exists(), f"File not found: {file_path.as_posix()!r}"
with open(file_path, "r") as file:
body = file.read()
geoserver.update_style("elevation", body=body, workspace="demo")
'Updated'
Uploading a style from an SLD file¶
# Get the styles associated to the raster layer
file_path = DATA_DIR / "styles" / "elevation.sld"
assert file_path.exists(), f"File not found: {file_path.as_posix()!r}"
geoserver.upload_style(file=file_path, workspace="demo", name="elevation_v2")
'Created'
Changing an existing style¶
To update a style, use the update_style
method.
# Get the styles associated to the raster layer
file_path = DATA_DIR / "styles" / "elevation.sld"
assert file_path.exists(), f"File not found: {file_path.as_posix()!r}"
with open(file_path, "r") as file:
body = file.read()
geoserver.update_style("elevation", body=body, workspace="demo")
'Updated'
Downloading a style¶
The SLD itself can be downloaded using the download_style
method.
xml = geoserver.download_style("elevation", workspace="demo")
print(xml)
<?xml version="1.0" encoding="UTF-8"?><sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:sld="http://www.opengis.net/sld" xmlns:gml="http://www.opengis.net/gml" xmlns:ogc="http://www.opengis.net/ogc" version="1.0.0"> <sld:NamedLayer> <sld:Name>raster</sld:Name> <sld:UserStyle> <sld:Name>raster</sld:Name> <sld:FeatureTypeStyle> <sld:Name>name</sld:Name> <sld:Rule> <sld:RasterSymbolizer> <sld:ChannelSelection> <sld:GrayChannel> <sld:SourceChannelName>1</sld:SourceChannelName> <sld:ContrastEnhancement> <sld:GammaValue>1.0</sld:GammaValue> </sld:ContrastEnhancement> </sld:GrayChannel> </sld:ChannelSelection> <sld:ColorMap> <sld:ColorMapEntry color="#FFFFFF" opacity="0" quantity="-1" label="label"/> <sld:ColorMapEntry color="#0000FF" opacity="1" quantity="0" label="label"/> <sld:ColorMapEntry color="#FF0000" opacity="1" quantity="110" label="label"/> </sld:ColorMap> <sld:ContrastEnhancement/> </sld:RasterSymbolizer> </sld:Rule> </sld:FeatureTypeStyle> </sld:UserStyle> </sld:NamedLayer> </sld:StyledLayerDescriptor>
Deleting a style¶
To delete a style, use the delete_style
method. You can use the purge=True
argument to remove the style and all its references.