GeoServer is an open source server for sharing geospatial data. It is often used to publish shapefiles and PostGIS tables (Vector data), ArcGrid and GeoTiff (Raster data). Moreover, it also allows for configuration of (cascading) remote WMS. Deltares maintains its own instance of GeoServer here. Data layers from GeoServer can be exposed in via WMS/WFS in many standard formats for subsequent public use. As NetCDF data format is widely used by the scientific community, ingesting that data through GeoServer tools represents a valid and useful alternative of data dissemination. Additional information on NetCDF data format can be found here.
This tutorial refers to GeoServer <stable> version 2.8.2 on Windows. Since the stable version of GeoServer does not allow ingestion of NetCDF as input Coverage Format, Geoserver Extension can be installed from http://ares.boundlessgeo.com/geoserver/ (Select corresponding extension version to Geoserver version downloaded on machine). Once the extension has been downloaded, the .jar files have to be copied to GeoServer 2.8.2\webapps\geoserver\WEB-INF\lib\
. For any of the following steps, the easiest way to work with NetCDF Coverage input is to use the user interface provided by GeoServer installation. However, for reasons of efficiency and process automation a client might prefer to use the REST API GeoServer provides. The REST configuration allows you to work with bindings from various programming languages or simply with cURL command line tool. Python bindings are also available from gsconfig.py, but there is no implementation of working with NetCDF files yet.
This is the first step before adding any resource, so that your project is kept tidy and structured. That has to be done prior to the store creation for our NetCDF resources.
First of all, create and add a workspace.
Add a workspace called ws
.
import os from geoserver.catalog import Catalog geoserverRestURL = "http://localhost:8090/geoserver/rest/" username = "admin" password = "geoserver" workspace = 'ws' cat = Catalog(geoserverRestURL, username, password) ws = cat.create_workspace(workspace, workspace) # # delete workspace # ws = cat.get_workspace(workspace) # cat.delete(ws) |
curl -u admin:geoserver -v -POST -H "Content-type: text/xml" -d "<workspace><name>ws</name></workspace>" "http://localhost:8090/geoserver/rest/workspaces" |
import os import requests workspace = 'ws' headers_xml = {'content-type': 'text/xml'} headers_zip = {'content-type': 'application/zip'} headers_sld = {'content-type': 'application/vnd.ogc.sld+xml'} r_create_workspace = requests.post("http://localhost:8090/geoserver/rest/workspaces", auth=('admin', 'geoserver'), data='<workspace><name>' + workspace + '</name></workspace>', headers=headers_xml) |
At the moment of writing this tutorial, the NetCDF plugin supports datasets where each variable’s axis is identified by an independent coordinate variable, therefore two dimensional non-independent latitude-longitude coordinate variables aren’t currently supported.
Once the workspace is created, you can add a NetCDF data store by following instructions and requirements on this page.
Add a store called st
. If you have a GeoTIFF, using Python bindings, cat.create_coveragestore()
is sufficient. As Python bindings do not support adding NetCDF data stores yet, cURL commands and Python requests are shown only.
REM Coordinate reference system CRS can be added as well, although not compulsory, under nativeCRS tags. Example: ...<nativeCRS>GEOGCS...</nativeCRS><srs>EPSG:4326</srs>... curl -u admin:geoserver -v -POST -H "Content-type: text/xml" -d "<coverageStore><name>st</name><workspace>ws</workspace><enabled>true</enable><type>NetCDF</type><url>d:\\YOURPATH\\data.nc</url></coverageStore>" "http://localhost:8090/geoserver/rest/workspaces/ws/coveragestores?configure=all" |
coveragestore = 'st' r_create_coveragestore = requests.post('http://localhost:8090/geoserver/rest/workspaces/ws/coveragestores?configure=all', auth=('admin', 'geoserver'), data='<coverageStore><name>' + coveragestore + '</name><workspace>' + workspace + "</workspace><enabled>true</enabled><type>NetCDF</type><url>" + "d:\\YOURPATH\\data.nc" + '</url></coverageStore>', headers=headers_xml) |
Adding a layer with GeoServer interface does not differ form adding any other type of layer. You can find a tutorial here.
Before creating a layer, the .nc
file has to be zipped so that it can be transferred to a Web server via application/zip
content type. The zip file will be opened in a folder inside the GeoServer data folder. Ancillary files will be created in that exact folder. Later on, the zip file can be deleted.
import zipfile # zip the nc file into a zip zfile = 'd:\\YOURPATH\\data.zip' output = zipfile.ZipFile(zfile, 'w') output.write('d:\\YOURPATH\\data.nc', coveragestore + '.nc', zipfile.ZIP_DEFLATED ) output.close() |
curl -u admin:geoserver -XPUT -H "Content-type:application/zip" --data-binary @d:\\YOURPATH\\data.zip http://localhost:8090/geoserver/rest/workspaces/ws/coveragestores/st/file.netcdf |
with open(output.filename, 'rb') as zip_file: r_create_layer = requests.put("http://localhost:8090/geoserver/rest/workspaces/" + workspace + "/coveragestores/" + coveragestore + "/file.netcdf", auth=('admin', 'geoserver'), data=zip_file, headers=headers_zip) |
At the end, remember to delete the zip file created for Web transfer.
import zipfile # deletes the zip created os.remove(zfile) |
If you are using GeoServer interface, the official manual describes very well how to modify layers, change names, or assign styles. As Restful interface might result harder to use, here are some examples.
Changing name to a layer can become convenient in case you have many NetCDF files with different names, though NetCDF variables have the exact same name, e.g. Band1
. Say you want to change Band1
into newBand
.
curl -v -u admin:geoserver -XPUT -H "Content-type: text/xml" -d "<coverage><name>newBand</name><enabled>true</enabled></coverage>" "http://localhost:8090/geoserver/rest/workspaces/ws/coveragestores/st/coverages/Band1" |
old_name_layer = "Band1" new_name_layer = "newBand" r_change_name = requests.put('http://localhost:8090/geoserver/rest/workspaces/' + workspace + "/coveragestores/" + coveragestore + "/coverages/" + old_name_layer, auth=('admin', 'geoserver'), data='<coverage><name>' + new_name_layer + '</name><enabled>true</enabled></coverage>', headers=headers_xml) |
Create a new style, upload it to GeoServer and assign to a workspace:layer
definition.
stylename = 'test' stylefilename = stylename + '.sld' styleallname = 'd:\\ROURPATH\\styles\\' + stylefilename # creates new style r_create_new_style = requests.post("http://localhost:8090/geoserver/rest/styles", auth=('admin', 'geoserver'), data='<style><name>' + stylename + '</name><filename>' + stylefilename + '</filename></style>', headers=headers_xml) # upload new style with open(styleallname, 'rb') as sld_file: r_upload_new_style = requests.put("http://localhost:8090/geoserver/rest/styles/" + stylename, auth=('admin', 'geoserver'), data=sld_file, headers=headers_sld) # assign it to a layer r_assign_new_style = requests.put("http://localhost:8090/geoserver/rest/layers/"+ workspace + ':' + new_name_layer, auth=('admin', 'geoserver'), data='<layer><defaultStyle><name>' + stylename + '</name></defaultStyle></layer>', headers=headers_xml) |