.. _mapscript_ows:
*****************************************************************************
MapScript Wrappers for WxS Services
*****************************************************************************
:Author: Frank Warmerdam
:Contact: warmerdam at pobox.com
:Revision: $Revision$
:Date: $Date$
.. contents::
:depth: 2
:backlinks: top
=============================================================================
Introduction
=============================================================================
With the implementation of MapServer :ref:`rfc16` in MapServer 4.9, MapScript
now has the ability to invoke MapServer's ability to execute OGC Web Service
requests such as WMS, WCS, and WFS as well as capturing the results of
processing the requests.
This makes it possible to dynamically configure a map object based on
information in the original request, and to capture the output of processing
requests for further post-processing.
=============================================================================
Python Examples
=============================================================================
The following trivial example, in Python, demonstrates a script that
internally provides the map name, but otherwise uses normal mapserver
processing.
.. code-block:: python
import mapscript
req = mapscript.OWSRequest()
req.loadParams()
map = mapscript.mapObj( '/u/www/maps/ukpoly/ukpoly.map' )
map.OWSDispatch( req )
The OWSRequest object is used to manage a parsed list of OWS processing
options. In the above example they are loaded from the environment using
the loadParams() call which fetches and parses them from QUERY_STRING in the
same way the :ref:`mapserv` executable would.
Then we load a map, and invoke OWSDispatch with the given arguments on that
map. By default the results of the dispatched request are written to stdout
which returns them back to the client.
The following example ignores all passed in arguments, and manually constructs
a request argument by argument. It is likely more useful for testing purposes
than for deploying WxS services, but demonstrates direct manipulation of the
request object.
.. code-block:: python
import mapscript
req = mapscript.OWSRequest()
req.setParameter( 'SERVICE', 'WMS' )
req.setParameter( 'VERSION', '1.1.0' )
req.setParameter( 'REQUEST', 'GetCapabilities' )
map = mapscript.mapObj( '/u/www/maps/ukpoly/ukpoly.map' )
map.OWSDispatch( req )
The previous example have all let results be returned directly to the client.
But in some cases we want to be able to capture, and perhaps modify the results
of our requests in some custom way. In the following example we force the
hated OGC required mime type for errors to simple text/xml (warning -
non-standard!)
.. code-block:: python
import mapscript
req = mapscript.OWSRequest()
req.loadParams()
map = mapscript.mapObj( '/u/www/maps/ukpoly/ukpoly.map' )
mapscript.msIO_installStdoutToBuffer()
map.OWSDispatch( req )
content_type = mapscript.msIO_stripStdoutBufferContentType()
content = mapscript.msIO_getStdoutBufferBytes()
if content_type == 'vnd.ogc.se_xml':
content_type = 'text/xml'
print 'Content-type: ' + content_type
print
print content
This example demonstrates capture capturing output of OWSRequest to
a buffer, capturing the "Content-type:" header value, and capturing the
actual content as binary data. The msIO_getStdoutBufferBytes() function
returns the stdout buffer as a byte array. If the result was known to
be text, the msIO_getStdoutBufferString() function could have been used to
fetch it as a string instead, for easier text manipulation.
=============================================================================
Perl Example
=============================================================================
Most of the same capabilities are accessable in all SWIG based mapscript
languages. In perl, we could script creation of a request like this:
.. code-block:: perl
#!/usr/bin/perl
use mapscript;
$req = new mapscript::OWSRequest();
$req->setParameter( "SERVICE", "WMS" );
$req->setParameter( "VERSION", "1.1.0" );
$req->setParameter( "REQUEST", "GetCapabilities" );
$map = new mapscript::mapObj( "/u/www/maps/ukpoly/ukpoly.map" );
mapscript::msIO_installStdoutToBuffer();
$dispatch_out = $map->OWSDispatch( $req );
printf "%s\n", mapscript::msIO_getStdoutBufferString();
One issue in Perl is that there is currently no wrapping for binary buffers
so you cannot call msIO_getStdoutBufferBytes(), and so cannot manipulate
binary results.
More Perl example code
----------------------
.. code-block:: perl
#!/usr/bin/perl
############################################################################
#
# Name: wxs.pl
# Project: MapServer
# Purpose: MapScript WxS example
#
# Author: Tom Kralidis
#
##############################################################################
#
# Copyright (c) 2007, Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies of this Software or works derived from this Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
############################################################################/
use CGI::Carp qw(fatalsToBrowser);
use mapscript;
use strict;
use warnings;
use XML::LibXSLT;
use XML::LibXML;
my $dispatch;
# uber-trivial XSLT document, as a file
my $xsltfile = "/tmp/foo.xslt";
# here's the actual document inline for
# testing save and alter $xsltFile above
=comment
=cut
my $mapfile = "/tmp/config.map";
# init OWSRequest object
my $req = new mapscript::OWSRequest();
# pick up CGI paramters passed
$req->loadParams();
# init mapfile
my $map = new mapscript::mapObj($mapfile);
# if this is a WFS GetCapabilities request, then intercept
# what is normally returned, process with an XSLT document
# and then return that to the client
if ($req->getValueByName('REQUEST') eq "GetCapabilities" && $req->getValueByName('SERVICE') eq "WFS") {
# push STDOUT to a buffer and run the incoming request
my $io = mapscript::msIO_installStdoutToBuffer();
$dispatch = $map->OWSDispatch($req);
# at this point, the client's request is sent
# pull out the HTTP headers
my $ct = mapscript::msIO_stripStdoutBufferContentType();
# and then pick up the actual content of the response
my $content = mapscript::msIO_getStdoutBufferString();
my $xml = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
# load XML content
my $source = $xml->parse_string($content);
# load XSLT document
my $style_doc = $xml->parse_file($xsltfile);
my $stylesheet = $xslt->parse_stylesheet($style_doc);
# invoke the XSLT transformation
my $results = $stylesheet->transform($source);
# print out the result (header + content)
print "Content-type: $ct\n\n";
print $stylesheet->output_string($results);
}
# else process as normal
else {
$dispatch = $map->OWSDispatch($req);
}
=============================================================================
Java Example
=============================================================================
One benefit of redirection of output to a buffer is that it is thread-safe.
Several threads in the same process can be actively processing requests and
writing their results to distinct output buffers. This Java example, used
to test multi-threaded access demonstrates that.
.. code-block:: java
import edu.umn.gis.mapscript.mapObj;
import edu.umn.gis.mapscript.OWSRequest;
import edu.umn.gis.mapscript.mapscript;
class WxSTest_thread extends Thread {
public String mapName;
public byte[] resultBytes;
public void run() {
mapObj map = new mapObj(mapName);
map.setMetaData( "ows_onlineresource", "http://dummy.org/" );
OWSRequest req = new OWSRequest();
req.setParameter( "SERVICE", "WMS" );
req.setParameter( "VERSION", "1.1.0" );
req.setParameter( "REQUEST", "GetCapabilities" );
mapscript.msIO_installStdoutToBuffer();
int owsResult = map.OWSDispatch( req );
if( owsResult != 0 )
System.out.println( "OWSDispatch Result (expect 0): " + owsResult );
resultBytes = mapscript.msIO_getStdoutBufferBytes();
}
}
public class WxSTest {
public static void main(String[] args) {
try {
WxSTest_thread tt[] = new WxSTest_thread[100];
int i;
int expectedLength=0, success = 0, failure=0;
for( i = 0; i < tt.length; i++ )
{
tt[i] = new WxSTest_thread();
tt[i].mapName = args[0];
}
for( i = 0; i < tt.length; i++ )
tt[i].start();
for( i = 0; i < tt.length; i++ )
{
tt[i].join();
if( i == 0 )
{
expectedLength = tt[i].resultBytes.length;
System.out.println( "Document Length: " + expectedLength + ", expecting somewhere around 10000 or more." );
}
else if( expectedLength != tt[i].resultBytes.length )
{
System.out.println( "Document Length:" + tt[i].resultBytes.length + " Expected:" + expectedLength );
failure++;
}
else
success++;
}
System.out.println( "Successes: " + success );
System.out.println( "Failures: " + failure );
} catch( Exception e ) {
e.printStackTrace();
}
}
}
=============================================================================
PHP Example
=============================================================================
Most of the same capabilities are accessible in php mapscript. Here
is an example displaying a :ref:`wms capabilities `.
.. code-block:: php
Example1 : get the capabilities
This is for example what a url could look like :
http://.../php/ows.php?service=WMS&version=1.1.1&Request=GetCapabilities
loadparams();
/*exampple on how to modify the parameters :
forcing the version from 1.1.1 to 1.1.0 */
$request->setParameter("VeRsIoN","1.1.0");
ms_ioinstallstdouttobuffer();
$oMap = ms_newMapobj("../../service/wms.map");
$oMap->owsdispatch($request);
$contenttype = ms_iostripstdoutbuffercontenttype();
$buffer = ms_iogetstdoutbufferstring();
header('Content-type: application/xml');
echo $buffer;
ms_ioresethandlers();
?>
Example2 : get the map
This is for example what a url could look like :
http://.../php/ows.php?SERVICE=WMS&VeRsIoN=1.1.1&Request=GetMap&
LAYERS=WorldGen_Outline
loadparams();
ms_ioinstallstdouttobuffer();
$oMap = ms_newMapobj("../../service/wms.map");
$oMap->owsdispatch($request);
$contenttype = ms_iostripstdoutbuffercontenttype();
if ($contenttype == 'image/png')
header('Content-type: image/png');
ms_iogetStdoutBufferBytes();
ms_ioresethandlers();
?>
=============================================================================
Use in Non-CGI Environments (mod_php, etc)
=============================================================================
The loadParams() call establish parses the cgi environment varabiables
(QUERY_STRING, and REQUEST_METHOD) into parameters in the OWSRequest object.
In non-cgi environments, such as when php, python and perl are used as
"loaded modules" in Apache, or Java with Tomcat, the loadParams() call
will not work - in fact in 4.10.x it will terminate the web server instance.
It is necessary in these circumstances for the calling script/application
to parse the request url into keyword/value pairs and assign to the
OWSRequest object by other means, as shown in some of the above examples
explicitly setting the request parameters.
=============================================================================
Post Processing Capabilities
=============================================================================
In the following python example, we process any incoming WxS request, but if it is
a GetCapabilities request we replace the Service section in the capabilities
with a section read from a file, that is carefully tailored the way we want.
.. code-block:: python
#!/usr/bin/env python
import sys
import elementtree.ElementTree as ET
import mapscript
req = mapscript.OWSRequest()
req.loadParams()
map = mapscript.mapObj( '/u/www/maps/ukpoly/ukpoly.map' )
#
# Handle anything but a GetCapabilities call normally.
#
if req.getValueByName('REQUEST') <> 'GetCapabilities':
map.OWSDispatch( req )
#
# Do special processing for GetCapabilities
#
else:
mapscript.msIO_installStdoutToBuffer()
map.OWSDispatch( req )
ct = mapscript.msIO_stripStdoutBufferContentType()
content = mapscript.msIO_getStdoutBufferString()
mapscript.msIO_resetHandlers()
# Parse the capabilities.
tree = ET.fromstring(content)
# Strip out ordinary Service section, and replace from custom file.
tree.remove(tree.find('Service'))
tree.insert(0,ET.parse('./Service.xml').getroot())
# Stream out adjusted capabilities.
print 'Content-type: ' + ct
print
print ET.tostring(tree)