.. index:: pair: WxS Services; Mapscript wrappers .. _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 <xsl:value-of select="wfs:Title"/> =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)