OSGeoLive-Notebooks/Mapserver/mapscript_quickstart.ipynb

443 lines
13 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# MapScript Quick Start\n",
"\n",
"Welcome to the Python MapScript quick start guide. \n",
"MapScript provides a programming interface to MapServer, and this notebook\n",
"provides an overview of its key functionality. \n",
"\n",
"## Mapfiles\n",
"The simplest way to use MapScript is to work with an existing [Mapfile](https://mapserver.org/mapfile/). \n",
"A new ```mapObj``` can be created by passing the path to a Mapfile. We will \n",
"be working with the Itasca demo map that is also used in the \n",
"[MapServer Demo](http://localhost/mapserver_demos/itasca/) on OSGeoLive. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"#sys.path.append(\"/rofs/usr/lib/python2.7/dist-packages\") # temporary hack for OSGeoLive\n",
"\n",
"import os\n",
"import mapscript\n",
"from IPython.display import Image\n",
"\n",
"#demo_fld = os.getenv(\"MAPSERVER_DEMO\")\n",
"#mapfile = os.path.join(demo_fld, \"itasca.map\")\n",
"\n",
"#osgeolive 13 \n",
"fname = '/usr/local/share/mapserver/demos/itasca/itasca.map'\n",
"map = mapscript.mapObj(fname)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Anything found in the Mapfile can be accessed and manipulated using MapScript. \n",
"For example we can get the count of all the layers in the Mapfile, and loop\n",
"through them printing out each layers name. \n",
"\n",
"MapScript objects are typically accessed using an index. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for idx in range(0, map.numlayers):\n",
" lyr = map.getLayer(idx)\n",
" print(lyr.name)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Drawing Maps\n",
"MapScript can be used to create an image file. The draw method\n",
"returns an imageObj which can be saved to a filename on disk. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import tempfile\n",
"# before creating images let's set the working directory to the temp folder\n",
"os.chdir(tempfile.gettempdir()) \n",
"\n",
"output_file = \"map.png\"\n",
"image = map.draw()\n",
"image.save(output_file)\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The map image above doesn't contain all the layers in the Mapfile. \n",
"This can be because they are set to hidden by default using ```LAYER STATUS OFF```.\n",
"\n",
"To turn on these layers and create a more interesting map, we \n",
"can loop through the layers again and set their ```STATUS``` to ```ON```. \n",
"We can then use the ```isVisible``` method to check if the layer will\n",
"be drawn onto the map. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for idx in range(0, map.numlayers):\n",
" lyr = map.getLayer(idx)\n",
" lyr.status = mapscript.MS_ON\n",
" print(lyr.name, lyr.isVisible())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You may notice that the *ctybdpy2* layer is still not visible even though\n",
"we set its ```STATUS``` to ```ON```. This is due to the ```REQUIRES``` keyword in its layer \n",
"definition that hides the layer if the *drgs* layer is displayed. \n",
"The *ctyrdln3* and *ctyrdln3_anno* layers are both hidden because of the ```MAXSCALE 300000```\n",
"layer setting. \n",
"\n",
"Now we can now draw the map again with the newly visible layers. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output_file = \"map_full.png\"\n",
"image = map.draw()\n",
"image.save(output_file)\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Other types of images can also be created from the ```mapObj```. These\n",
"use the same process of creating an ```imageObj``` and saving it to disk. \n",
"\n",
"For example to create a legend image:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"output_file = \"map_legend.png\"\n",
"legend_img = map.drawLegend()\n",
"legend_img.save(output_file)\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Querying Maps\n",
"As well as drawing maps using MapScript we can also query the data\n",
"referenced by the layers. In this example we will be finding the\n",
"layer to query using its name, and then querying the ```NAME``` field to find\n",
"the name of an airport. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"qry_layer = map.getLayerByName('airports')\n",
"qry_layer.queryByAttributes(qry_layer.map, \"NAME\", \"Bowstring Municipal Airport\", \n",
" mapscript.MS_SINGLE)\n",
"\n",
"results = qry_layer.getResults()\n",
"assert results.numresults == 1 # as we did a single query (using MS_SINGLE) there should be only one result\n",
"result = results.getResult(0)\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Query results are stored as ```resultCacheObj```. These contain a reference to the\n",
"result feature - a ```shapeObj```. The ```shapeObj``` can access both the geometry and \n",
"attributes of a feature. \n",
"\n",
"Let's get the ```shapeObj``` from the ```resultCacheObj``` and \n",
"loop through the shapes attributes to store them in a list. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"result_shp = qry_layer.getShape(result)\n",
"\n",
"values = []\n",
"for idx in range(0, result_shp.numvalues):\n",
" values.append(result_shp.getValue(idx))\n",
"\n",
"print(values)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It would be nice to have also the property names alongside the values. Field names\n",
"are stored in the layer in which the ```shapeObj``` belongs, and not in the ```shapeObj```\n",
"itself. We can get a list of fields from the layer, and then use the Python ```zip``` function\n",
"to join them with the shape values: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fields = []\n",
"for idx in range(0, qry_layer.numitems):\n",
" fields.append(qry_layer.getItem(idx))\n",
"\n",
"print(fields)\n",
"props = zip(fields, values) # join fields to values\n",
"print(props)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also create a map showing the query results: \n",
"*Note the imageObj is broken for Python MapScript 7.0, but is fixed in 7.2*"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# create a new 400 by 400 empty image\n",
"query_image = mapscript.imageObj(400, 400)\n",
"# draw the query into the image and save it to file\n",
"qry_layer.drawQuery(qry_layer.map, query_image)\n",
"output_file = r\"layer_query.png\"\n",
"query_image.save(output_file)\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we want to zoom in on the results we can set the map extent to a buffered area\n",
"around the results: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"bbox = result_shp.bounds\n",
"print(bbox.minx, bbox.miny, bbox.maxx, bbox.maxy)\n",
"buffer = 2000\n",
"\n",
"map.getLayerByName('drgs').status = mapscript.MS_OFF # hide the raster layer for this map\n",
"map.setExtent(bbox.minx - buffer, bbox.miny - buffer, bbox.maxx + buffer, bbox.maxy + buffer)\n",
"\n",
"output_file = r\"map_query.png\"\n",
"image = map.draw()\n",
"image.save(output_file)\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## OGC Web Services\n",
"\n",
"MapScript can also be used to send requests to MapServer OWS capabilities, to \n",
"query WMS and WFS services. First we will get the WMS GetCapabilities XML for the map: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ows_req = mapscript.OWSRequest()\n",
"ows_req.type = mapscript.MS_GET_REQUEST\n",
"ows_req.setParameter(\"SERVICE\", \"WMS\");\n",
"ows_req.setParameter(\"VERSION\", \"1.3.0\");\n",
"ows_req.setParameter(\"REQUEST\", \"GetCapabilities\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We use the msIO methods to capture the response the request\n",
"that is sent to ```stdout```. \n",
"The response is typically an HTTP response with HTTP content headers. \n",
"We can strip these out using MapScript"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mapscript.msIO_installStdoutToBuffer()\n",
"map.OWSDispatch(ows_req)\n",
"content_type = mapscript.msIO_stripStdoutBufferContentType()\n",
"# remove the content type header from the XML\n",
"mapscript.msIO_stripStdoutBufferContentHeaders() # Strip all Content-* headers\n",
"result = mapscript.msIO_getStdoutBufferBytes()\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also retrieve images from a WMS service. \n",
"Rather than setting lots of individual parameters we can simply load them from\n",
"a string in the same format was would be sent via a web client. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# First let's get the extent of the map to use in the request\n",
"extent = map.extent\n",
"print(extent)\n",
"\n",
"bbox = \"BBOX={},{},{},{}\".format(extent.minx, extent.miny, extent.maxx, extent.maxy)\n",
"querystring = \"SERVICE=WMS&REQUEST=GetMap&VERSION=1.3.0&LAYERS=lakespy2&STYLES=&CRS=EPSG:26915&FORMAT=image/png&WIDTH=400&HEIGHT=400&{}\".format(bbox)\n",
"\n",
"ows_req = mapscript.OWSRequest()\n",
"ows_req.loadParamsFromURL(querystring)\n",
"success = map.OWSDispatch(ows_req)\n",
"assert success == mapscript.MS_SUCCESS\n",
"\n",
"# clear the HTTP headers or we will have an invalid image\n",
"headers = mapscript.msIO_getAndStripStdoutBufferMimeHeaders()\n",
"result = mapscript.msIO_getStdoutBufferBytes()\n",
"\n",
"output_file = \"wms.png\"\n",
"with open(output_file, \"wb\") as f:\n",
" f.write(result)\n",
"\n",
"Image(filename=output_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally let's get the SLD for one of the layers in the map: "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"lines_to_next_cell": 2
},
"outputs": [],
"source": [
"lakes_layer = map.getLayerByName('lakespy2')\n",
"result = lakes_layer.generateSLD()\n",
"print(result)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Thanks for working through this notebook! For more information on MapScript\n",
"please see the [MapScript documentation](https://mapserver.org/mapscript/introduction.html). \n",
"Additional Python examples can be found in the [MapServer GitHub repository](https://github.com/mapserver/mapserver/tree/master/mapscript/python/examples)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"jupytext": {
"formats": "ipynb,pct.py:percent,lgt.py:light,spx.py:sphinx,md,Rmd",
"text_representation": {
"extension": ".py",
"format_name": "percent",
"format_version": "1.1",
"jupytext_version": "0.8.0"
}
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}