443 lines
13 KiB
Plaintext
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
|
|
}
|