OGCServer now lives at https://github.com/mapnik/OGCServer, remove from python bindings

This commit is contained in:
Dane Springmeyer 2011-11-23 20:27:54 -08:00
parent 14700dba16
commit d7ac269a6c
10 changed files with 0 additions and 1621 deletions

View file

@ -1,125 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""Interface for registering map styles and layers for availability in WMS Requests."""
from common import Version, copy_style, copy_layer
from exceptions import OGCException, ServerConfigurationError
from wms111 import ServiceHandler as ServiceHandler111
from wms130 import ServiceHandler as ServiceHandler130
from mapnik import Style, Map, load_map
import re
import sys
def ServiceHandlerFactory(conf, mapfactory, onlineresource, version):
if not version:
version = Version('1.3.0')
else:
version = Version(version)
if version >= '1.3.0':
return ServiceHandler130(conf, mapfactory, onlineresource)
else:
return ServiceHandler111(conf, mapfactory, onlineresource)
class BaseWMSFactory:
def __init__(self):
self.layers = {}
self.ordered_layers = []
self.styles = {}
self.aggregatestyles = {}
def loadXML(self, xmlfile, strict=False):
tmp_map = Map(0,0)
load_map (tmp_map, xmlfile, strict)
for lyr in tmp_map.layers:
style_count = len(lyr.styles)
if style_count == 0:
raise ServerConfigurationError("Cannot register Layer '%s' without a style" % lyr.name)
elif style_count == 1:
style_obj = tmp_map.find_style(lyr.styles[0])
style_obj = copy_style(style_obj)
style_name = lyr.styles[0]
if style_name not in self.aggregatestyles.keys() and style_name not in self.styles.keys():
self.register_style(style_name, style_obj)
self.register_layer(copy_layer(lyr), style_name, extrastyles=(style_name,))
elif style_count > 1:
for style_name in lyr.styles:
style_obj = tmp_map.find_style(style_name)
style_obj = copy_style(style_obj)
if style_name not in self.aggregatestyles.keys() and style_name not in self.styles.keys():
self.register_style(style_name, style_obj)
aggregates = tuple([sty for sty in lyr.styles])
aggregates_name = '%s_aggregates' % lyr.name
self.register_aggregate_style(aggregates_name,aggregates)
self.register_layer(copy_layer(lyr), aggregates_name, extrastyles=aggregates)
def register_layer(self, layer, defaultstyle, extrastyles=()):
layername = layer.name
if not layername:
raise ServerConfigurationError('Attempted to register an unnamed layer.')
if not re.match('^\+init=epsg:\d+$', layer.srs) and not re.match('^\+proj=.*$', layer.srs):
raise ServerConfigurationError('Attempted to register a layer without an epsg projection defined.')
if defaultstyle not in self.styles.keys() + self.aggregatestyles.keys():
raise ServerConfigurationError('Attempted to register a layer with an non-existent default style.')
layer.wmsdefaultstyle = defaultstyle
if isinstance(extrastyles, tuple):
for stylename in extrastyles:
if type(stylename) == type(''):
if stylename not in self.styles.keys() + self.aggregatestyles.keys():
raise ServerConfigurationError('Attempted to register a layer with an non-existent extra style.')
else:
ServerConfigurationError('Attempted to register a layer with an invalid extra style name.')
layer.wmsextrastyles = extrastyles
else:
raise ServerConfigurationError('Layer "%s" was passed an invalid list of extra styles. List must be a tuple of strings.' % layername)
self.ordered_layers.append(layer)
self.layers[layername] = layer
def register_style(self, name, style):
if not name:
raise ServerConfigurationError('Attempted to register a style without providing a name.')
if name in self.aggregatestyles.keys() or name in self.styles.keys():
raise ServerConfigurationError("Attempted to register a style with a name already in use: '%s'" % name)
if not isinstance(style, Style):
raise ServerConfigurationError('Bad style object passed to register_style() for style "%s".' % name)
self.styles[name] = style
def register_aggregate_style(self, name, stylenames):
if not name:
raise ServerConfigurationError('Attempted to register an aggregate style without providing a name.')
if name in self.aggregatestyles.keys() or name in self.styles.keys():
raise ServerConfigurationError('Attempted to register an aggregate style with a name already in use.')
self.aggregatestyles[name] = []
for stylename in stylenames:
if stylename not in self.styles.keys():
raise ServerConfigurationError('Attempted to register an aggregate style containing a style that does not exist.')
self.aggregatestyles[name].append(stylename)
def finalize(self):
if len(self.layers) == 0:
raise ServerConfigurationError('No layers defined!')
if len(self.styles) == 0:
raise ServerConfigurationError('No styles defined!')
for layer in self.layers.values():
for style in list(layer.styles) + list(layer.wmsextrastyles):
if style not in self.styles.keys() + self.aggregatestyles.keys():
raise ServerConfigurationError('Layer "%s" refers to undefined style "%s".' % (layer.name, style))

View file

@ -1,28 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""Mapnik OGC WMS Server."""
import os
import warnings
warnings.warn("ogcserver module development has moved to https://github.com/mapnik/OGCServer.\n This code will function fine with this version, but will be removed in Mapnik 2.1.0. Disable this warning by editing this file: %s" % os.path.realpath(__file__), DeprecationWarning, 2)

View file

@ -1,116 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""CGI/FastCGI handler for Mapnik OGC WMS Server.
Requires 'jon' module.
"""
from os import environ
from tempfile import gettempdir
environ['PYTHON_EGG_CACHE'] = gettempdir()
import sys
from jon import cgi
from exceptions import OGCException, ServerConfigurationError
from wms111 import ExceptionHandler as ExceptionHandler111
from wms130 import ExceptionHandler as ExceptionHandler130
from configparser import SafeConfigParser
from common import Version
class Handler(cgi.DebugHandler):
def __init__(self):
conf = SafeConfigParser()
conf.readfp(open(self.configpath))
self.conf = conf
if not conf.has_option_with_value('server', 'module'):
raise ServerConfigurationError('The factory module is not defined in the configuration file.')
try:
mapfactorymodule = __import__(conf.get('server', 'module'))
except ImportError:
raise ServerConfigurationError('The factory module could not be loaded.')
if hasattr(mapfactorymodule, 'WMSFactory'):
self.mapfactory = getattr(mapfactorymodule, 'WMSFactory')()
else:
raise ServerConfigurationError('The factory module does not have a WMSFactory class.')
if conf.has_option('server', 'debug'):
self.debug = int(conf.get('server', 'debug'))
else:
self.debug = 0
def process(self, req):
reqparams = lowerparams(req.params)
onlineresource = 'http://%s:%s%s?' % (req.environ['SERVER_NAME'], req.environ['SERVER_PORT'], req.environ['SCRIPT_NAME'])
if not reqparams.has_key('request'):
raise OGCException('Missing request parameter.')
request = reqparams['request']
del reqparams['request']
if request == 'GetCapabilities' and not reqparams.has_key('service'):
raise OGCException('Missing service parameter.')
if request in ['GetMap', 'GetFeatureInfo']:
service = 'WMS'
else:
service = reqparams['service']
if reqparams.has_key('service'):
del reqparams['service']
try:
mapnikmodule = __import__('mapnik.ogcserver.' + service)
except:
raise OGCException('Unsupported service "%s".' % service)
ServiceHandlerFactory = getattr(mapnikmodule.ogcserver, service).ServiceHandlerFactory
servicehandler = ServiceHandlerFactory(self.conf, self.mapfactory, onlineresource, reqparams.get('version', None))
if reqparams.has_key('version'):
del reqparams['version']
if request not in servicehandler.SERVICE_PARAMS.keys():
raise OGCException('Operation "%s" not supported.' % request, 'OperationNotSupported')
ogcparams = servicehandler.processParameters(request, reqparams)
try:
requesthandler = getattr(servicehandler, request)
except:
raise OGCException('Operation "%s" not supported.' % request, 'OperationNotSupported')
response = requesthandler(ogcparams)
req.set_header('Content-Type', response.content_type)
req.set_header('Content-Length', str(len(response.content)))
req.write(response.content)
def traceback(self, req):
reqparams = lowerparams(req.params)
version = reqparams.get('version', None)
if not version:
version = Version('1.3.0')
else:
version = Version(version)
if version >= '1.3.0':
eh = ExceptionHandler130(self.debug)
else:
eh = ExceptionHandler111(self.debug)
response = eh.getresponse(reqparams)
req.set_header('Content-Type', response.content_type)
req.set_header('Content-Length', str(len(response.content)))
req.write(response.content)
def lowerparams(params):
reqparams = {}
for key, value in params.items():
reqparams[key.lower()] = value
return reqparams

View file

@ -1,539 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""Core OGCServer classes and functions."""
from exceptions import OGCException, ServerConfigurationError
from mapnik import Map, Color, Box2d, render, Image, Layer, Style, Projection as MapnikProjection, Coord
from PIL.Image import new
from PIL.ImageDraw import Draw
from StringIO import StringIO
from copy import deepcopy
from traceback import format_exception, format_exception_only
from sys import exc_info
import re
import sys
try:
from lxml import etree as ElementTree
except ImportError:
import xml.etree.ElementTree as ElementTree
except ImportError:
import elementtree.ElementTree as ElementTree
# from elementtree import ElementTree
# ElementTree._namespace_map.update({'http://www.opengis.net/wms': 'wms',
# 'http://www.opengis.net/ogc': 'ogc',
# 'http://www.w3.org/1999/xlink': 'xlink',
# 'http://www.w3.org/2001/XMLSchema-instance': 'xsi'
# })
PIL_TYPE_MAPPING = {'image/jpeg': 'jpeg', 'image/png': 'png'}
class ParameterDefinition:
def __init__(self, mandatory, cast, default=None, allowedvalues=None, fallback=False):
""" An OGC request parameter definition. Used to describe a
parameter's characteristics.
@param mandatory: Is this parameter required by the request?
@type mandatory: Boolean.
@param default: Default value to use if one is not provided
and the parameter is optional.
@type default: None or any valid value.
@param allowedvalues: A list of allowed values for the parameter.
If a value is provided that is not in this
list, an error is raised.
@type allowedvalues: A python tuple of values.
@param fallback: Whether the value of the parameter should fall
back to the default should an illegal value be
provided.
@type fallback: Boolean.
@return: A L{ParameterDefinition} instance.
"""
if mandatory not in [True, False]:
raise ServerConfigurationError("Bad value for 'mandatory' parameter, must be True or False.")
self.mandatory = mandatory
if not callable(cast):
raise ServerConfigurationError('Cast parameter definition must be callable.')
self.cast = cast
self.default = default
if allowedvalues and type(allowedvalues) != type(()):
raise ServerConfigurationError("Bad value for 'allowedvalues' parameter, must be a tuple.")
self.allowedvalues = allowedvalues
if fallback not in [True, False]:
raise ServerConfigurationError("Bad value for 'fallback' parameter, must be True or False.")
self.fallback = fallback
class BaseServiceHandler:
CONF_CONTACT_PERSON_PRIMARY = [
['contactperson', 'ContactPerson', str],
['contactorganization', 'ContactOrganization', str]
]
CONF_CONTACT_ADDRESS = [
['addresstype', 'AddressType', str],
['address', 'Address', str],
['city', 'City', str],
['stateorprovince', 'StateOrProvince', str],
['postcode', 'PostCode', str],
['country', 'Country', str]
]
CONF_CONTACT = [
['contactposition', 'ContactPosition', str],
['contactvoicetelephone', 'ContactVoiceTelephone', str],
['contactelectronicmailaddress', 'ContactElectronicMailAddress', str]
]
def processParameters(self, requestname, params):
finalparams = {}
for paramname, paramdef in self.SERVICE_PARAMS[requestname].items():
if paramname not in params.keys() and paramdef.mandatory:
raise OGCException('Mandatory parameter "%s" missing from request.' % paramname)
elif paramname in params.keys():
try:
params[paramname] = paramdef.cast(params[paramname])
except OGCException:
raise
except:
raise OGCException('Invalid value "%s" for parameter "%s".' % (params[paramname], paramname))
if paramdef.allowedvalues and params[paramname] not in paramdef.allowedvalues:
if not paramdef.fallback:
raise OGCException('Parameter "%s" has an illegal value.' % paramname)
else:
finalparams[paramname] = paramdef.default
else:
finalparams[paramname] = params[paramname]
elif not paramdef.mandatory and paramdef.default:
finalparams[paramname] = paramdef.default
return finalparams
def processServiceCapabilities(self, capetree):
if len(self.conf.items('service')) > 0:
servicee = capetree.find('{http://www.opengis.net/wms}Service')
for item in self.CONF_SERVICE:
if self.conf.has_option_with_value('service', item[0]):
value = self.conf.get('service', item[0]).strip()
try:
item[2](value)
except:
raise ServerConfigurationError('Configuration parameter [%s]->%s has an invalid value: %s.' % ('service', item[0], value))
if item[0] == 'onlineresource':
element = ElementTree.Element('%s' % item[1])
servicee.append(element)
element.set('{http://www.w3.org/1999/xlink}href', value)
element.set('{http://www.w3.org/1999/xlink}type', 'simple')
elif item[0] == 'keywordlist':
element = ElementTree.Element('%s' % item[1])
servicee.append(element)
keywords = value.split(',')
keywords = map(str.strip, keywords)
for keyword in keywords:
kelement = ElementTree.Element('Keyword')
kelement.text = keyword
element.append(kelement)
else:
element = ElementTree.Element('%s' % item[1])
element.text = value
servicee.append(element)
if len(self.conf.items_with_value('contact')) > 0:
element = ElementTree.Element('ContactInformation')
servicee.append(element)
for item in self.CONF_CONTACT:
if self.conf.has_option_with_value('contact', item[0]):
value = self.conf.get('contact', item[0]).strip()
try:
item[2](value)
except:
raise ServerConfigurationError('Configuration parameter [%s]->%s has an invalid value: %s.' % ('service', item[0], value))
celement = ElementTree.Element('%s' % item[1])
celement.text = value
element.append(celement)
for item in self.CONF_CONTACT_PERSON_PRIMARY + self.CONF_CONTACT_ADDRESS:
if item in self.CONF_CONTACT_PERSON_PRIMARY:
tagname = 'ContactPersonPrimary'
else:
tagname = 'ContactAddress'
if self.conf.has_option_with_value('contact', item[0]):
if element.find(tagname) == None:
subelement = ElementTree.Element(tagname)
element.append(subelement)
value = self.conf.get('contact', item[0]).strip()
try:
item[2](value)
except:
raise ServerConfigurationError('Configuration parameter [%s]->%s has an invalid value: %s.' % ('service', item[0], value))
celement = ElementTree.Element('%s' % item[1])
celement.text = value
subelement.append(celement)
class Response:
def __init__(self, content_type, content):
self.content_type = content_type
self.content = content
class Version:
def __init__(self, version):
version = version.split('.')
if len(version) != 3:
raise OGCException('Badly formatted version number.')
try:
version = map(int, version)
except:
raise OGCException('Badly formatted version number.')
self.version = version
def __repr__(self):
return '%s.%s.%s' % (self.version[0], self.version[1], self.version[2])
def __cmp__(self, other):
if isinstance(other, str):
other = Version(other)
if self.version[0] < other.version[0]:
return -1
elif self.version[0] > other.version[0]:
return 1
else:
if self.version[1] < other.version[1]:
return -1
elif self.version[1] > other.version[1]:
return 1
else:
if self.version[2] < other.version[2]:
return -1
elif self.version[2] > other.version[2]:
return 1
else:
return 0
class ListFactory:
def __init__(self, cast):
self.cast = cast
def __call__(self, string):
seq = string.split(',')
return map(self.cast, seq)
def ColorFactory(colorstring):
if re.match('^0x[a-fA-F0-9]{6}$', colorstring):
return Color(eval('0x' + colorstring[2:4]), eval('0x' + colorstring[4:6]), eval('0x' + colorstring[6:8]))
else:
raise OGCException('Invalid color value. Must be of format "0xFFFFFF".')
class CRS:
def __init__(self, namespace, code):
self.namespace = namespace.lower()
self.code = int(code)
self.proj = None
def __repr__(self):
return '%s:%s' % (self.namespace, self.code)
def __eq__(self, other):
if str(other) == str(self):
return True
return False
def inverse(self, x, y):
if not self.proj:
self.proj = Projection('+init=%s:%s' % (self.namespace, self.code))
return self.proj.inverse(Coord(x, y))
def forward(self, x, y):
if not self.proj:
self.proj = Projection('+init=%s:%s' % (self.namespace, self.code))
return self.proj.forward(Coord(x, y))
class CRSFactory:
def __init__(self, allowednamespaces):
self.allowednamespaces = allowednamespaces
def __call__(self, crsstring):
if not re.match('^[A-Z]{3,5}:\d+$', crsstring):
raise OGCException('Invalid format for the CRS parameter: %s' % crsstring, 'InvalidCRS')
crsparts = crsstring.split(':')
if crsparts[0] in self.allowednamespaces:
return CRS(crsparts[0], crsparts[1])
else:
raise OGCException('Invalid CRS Namespace: %s' % crsparts[0], 'InvalidCRS')
def copy_layer(obj):
lyr = Layer(obj.name)
lyr.abstract = obj.abstract
lyr.active = obj.active
lyr.clear_label_cache = obj.clear_label_cache
lyr.datasource = obj.datasource
#lyr.maxzoom = obj.maxzoom
#lyr.minzoom = obj.minzoom
lyr.queryable = obj.queryable
lyr.srs = obj.srs
lyr.title = obj.title
if hasattr(obj,'wmsdefaultstyle'):
lyr.wmsdefaultstyle = obj.wmsdefaultstyle
if hasattr(obj,'wmsextrastyles'):
lyr.wmsextrastyles = obj.wmsextrastyles
return lyr
def copy_style(obj):
sty = Style()
for rule in obj.rules:
sty.rules.append(rule)
return sty
class WMSBaseServiceHandler(BaseServiceHandler):
def GetMap(self, params):
m = self._buildMap(params)
im = Image(params['width'], params['height'])
render(m, im)
return Response(params['format'], im.tostring(PIL_TYPE_MAPPING[params['format']]))
def GetFeatureInfo(self, params, querymethodname='query_point'):
m = self._buildMap(params)
if params['info_format'] == 'text/plain':
writer = TextFeatureInfo()
elif params['info_format'] == 'text/xml':
writer = XMLFeatureInfo()
if params['query_layers'] and params['query_layers'][0] == '__all__':
for layerindex, layer in enumerate(m.layers):
featureset = getattr(m, querymethodname)(layerindex, params['i'], params['j'])
features = featureset.features
if features:
writer.addlayer(layer.name)
for feat in features:
writer.addfeature()
for prop in feat.properties:
writer.addattribute(prop[0], prop[1])
else:
for layerindex, layername in enumerate(params['query_layers']):
if layername in params['layers']:
if m.layers[layerindex].queryable:
featureset = getattr(m, querymethodname)(layerindex, params['i'], params['j'])
features = featureset.features
if features:
writer.addlayer(m.layers[layerindex].name)
for feat in features:
writer.addfeature()
for prop in feat.properties:
writer.addattribute(prop[0], prop[1])
else:
raise OGCException('Requested query layer "%s" is not marked queryable.' % layername, 'LayerNotQueryable')
else:
raise OGCException('Requested query layer "%s" not in the LAYERS parameter.' % layername)
return Response(params['info_format'], str(writer))
def _buildMap(self, params):
if str(params['crs']) not in self.allowedepsgcodes:
raise OGCException('Unsupported CRS "%s" requested.' % str(params['crs']).upper(), 'InvalidCRS')
if params['bbox'][0] >= params['bbox'][2]:
raise OGCException("BBOX values don't make sense. minx is greater than maxx.")
if params['bbox'][1] >= params['bbox'][3]:
raise OGCException("BBOX values don't make sense. miny is greater than maxy.")
if params.has_key('styles') and len(params['styles']) != len(params['layers']):
raise OGCException('STYLES length does not match LAYERS length.')
m = Map(params['width'], params['height'], '+init=%s' % params['crs'])
if params.has_key('transparent') and params['transparent'] == 'FALSE':
if params['bgcolor']:
m.background = params['bgcolor']
else:
m.background = Color(0, 0, 0, 0)
maplayers = self.mapfactory.layers
orderedmaplayers = self.mapfactory.ordered_layers
mapstyles = self.mapfactory.styles
mapaggregatestyles = self.mapfactory.aggregatestyles
# a non WMS spec way of requesting all layers
if params['layers'] and params['layers'][0] == '__all__':
for layername in orderedmaplayers:
layer = copy_layer(layername)
reqstyle = layer.wmsdefaultstyle
if reqstyle in mapaggregatestyles.keys():
for stylename in mapaggregatestyles[reqstyle]:
layer.styles.append(stylename)
else:
layer.styles.append(reqstyle)
for stylename in layer.styles:
if stylename in mapstyles.keys():
m.append_style(stylename, mapstyles[stylename])
m.layers.append(layer)
else:
for layerindex, layername in enumerate(params['layers']):
try:
layer = copy_layer(maplayers[layername])
except KeyError:
raise OGCException('Layer "%s" not defined.' % layername, 'LayerNotDefined')
try:
reqstyle = params['styles'][layerindex]
except IndexError:
reqstyle = ''
if reqstyle and reqstyle not in layer.wmsextrastyles:
raise OGCException('Invalid style "%s" requested for layer "%s".' % (reqstyle, layername), 'StyleNotDefined')
if not reqstyle:
reqstyle = layer.wmsdefaultstyle
if reqstyle in mapaggregatestyles.keys():
for stylename in mapaggregatestyles[reqstyle]:
layer.styles.append(stylename)
else:
layer.styles.append(reqstyle)
for stylename in layer.styles:
if stylename in mapstyles.keys():
m.append_style(stylename, mapstyles[stylename])
else:
raise ServerConfigurationError('Layer "%s" refers to non-existent style "%s".' % (layername, stylename))
m.layers.append(layer)
m.zoom_to_box(Box2d(params['bbox'][0], params['bbox'][1], params['bbox'][2], params['bbox'][3]))
return m
class BaseExceptionHandler:
def __init__(self, debug):
self.debug = debug
def getresponse(self, params):
code = ''
message = '\n'
if not params:
message = '''
<h2>Welcome to the Mapnik OGCServer.</h2>
<h3>Ready to accept map requests...</h5>
<h4>For more info see: <a href="http://trac.mapnik.org/wiki/OgcServer">trac.mapnik.org</a></h4>
'''
return self.htmlhandler('', message)
excinfo = exc_info()
if self.debug:
messagelist = format_exception(excinfo[0], excinfo[1], excinfo[2])
else:
messagelist = format_exception_only(excinfo[0], excinfo[1])
message += ''.join(messagelist)
if isinstance(excinfo[1], OGCException) and len(excinfo[1].args) > 1:
code = excinfo[1].args[1]
exceptions = params.get('exceptions', None)
if self.debug:
return self.htmlhandler(code, message)
if not exceptions or not self.handlers.has_key(exceptions):
exceptions = self.defaulthandler
return self.handlers[exceptions](self, code, message, params)
def htmlhandler(self,code,message):
if code:
resp_text = '<h2>OGCServer Error:</h2><pre>%s</pre>\n<h3>Traceback:</h3><pre>%s</pre>\n' % (message, code)
else:
resp_text = message
return Response('text/html', resp_text)
def xmlhandler(self, code, message, params):
ogcexcetree = deepcopy(self.xmltemplate)
e = ogcexcetree.find(self.xpath)
e.text = message
if code:
e.set('code', code)
return Response(self.xmlmimetype, ElementTree.tostring(ogcexcetree))
def inimagehandler(self, code, message, params):
im = new('RGBA', (int(params['width']), int(params['height'])))
im.putalpha(new('1', (int(params['width']), int(params['height']))))
draw = Draw(im)
for count, line in enumerate(message.strip().split('\n')):
draw.text((12,15*(count+1)), line, fill='#000000')
fh = StringIO()
im.save(fh, PIL_TYPE_MAPPING[params['format']])
fh.seek(0)
return Response(params['format'], fh.read())
def blankhandler(self, code, message, params):
bgcolor = params.get('bgcolor', '#FFFFFF')
bgcolor = bgcolor.replace('0x', '#')
transparent = params.get('transparent', 'FALSE')
if transparent == 'TRUE':
im = new('RGBA', (int(params['width']), int(params['height'])))
im.putalpha(new('1', (int(params['width']), int(params['height']))))
else:
im = new('RGBA', (int(params['width']), int(params['height'])), bgcolor)
fh = StringIO()
im.save(fh, PIL_TYPE_MAPPING[params['format']])
fh.seek(0)
return Response(params['format'], fh.read())
class Projection(MapnikProjection):
def epsgstring(self):
return self.params().split('=')[1].upper()
class TextFeatureInfo:
def __init__(self):
self.buffer = ''
def addlayer(self, name):
self.buffer += '\n[%s]\n' % name
def addfeature(self):
pass#self.buffer += '\n'
def addattribute(self, name, value):
self.buffer += '%s=%s\n' % (name, str(value))
def __str__(self):
return self.buffer
class XMLFeatureInfo:
basexml = """<?xml version="1.0"?>
<resultset>
</resultset>
"""
def __init__(self):
self.rootelement = ElementTree.fromstring(self.basexml)
def addlayer(self, name):
layer = ElementTree.Element('layer')
layer.set('name', name)
self.rootelement.append(layer)
self.currentlayer = layer
def addfeature(self):
feature = ElementTree.Element('feature')
self.currentlayer.append(feature)
self.currentfeature = feature
def addattribute(self, name, value):
attribute = ElementTree.Element('attribute')
attname = ElementTree.Element('name')
attname.text = name
attvalue = ElementTree.Element('value')
attvalue.text = unicode(value)
attribute.append(attname)
attribute.append(attvalue)
self.currentfeature.append(attribute)
def __str__(self):
return '<?xml version="1.0"?>\n' + ElementTree.tostring(self.rootelement)

View file

@ -1,44 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
""" Change SafeConfigParser behavior to treat options without values as
non-existent.
"""
from ConfigParser import SafeConfigParser as OrigSafeConfigParser
class SafeConfigParser(OrigSafeConfigParser):
def items_with_value(self, section):
finallist = []
items = self.items(section)
for item in items:
if item[1] != '':
finallist.append(item)
return finallist
def has_option_with_value(self, section, option):
if self.has_option(section, option):
if self.get(section, option) == '':
return False
else:
return False
return True

View file

@ -1,28 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""Custom OGCServer Exceptions"""
class OGCException(Exception):
pass
class ServerConfigurationError(Exception):
pass

View file

@ -1,133 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id: modserver.py 283 2006-07-22 18:54:53Z jdoyon $
"""Mod_python handler for Mapnik OGC WMS Server."""
import sys
from mod_python import apache, util
from exceptions import OGCException, ServerConfigurationError
from wms111 import ExceptionHandler as ExceptionHandler111
from wms130 import ExceptionHandler as ExceptionHandler130
from configparser import SafeConfigParser
from common import Version
class ModHandler(object):
def __init__(self, configpath):
conf = SafeConfigParser()
conf.readfp(open(configpath))
self.conf = conf
if not conf.has_option_with_value('server', 'module'):
raise ServerConfigurationError('The factory module is not defined in the configuration file.')
try:
mapfactorymodule = __import__(conf.get('server', 'module'))
except ImportError:
raise ServerConfigurationError('The factory module could not be loaded.')
if hasattr(mapfactorymodule, 'WMSFactory'):
self.mapfactory = getattr(mapfactorymodule, 'WMSFactory')()
else:
raise ServerConfigurationError('The factory module does not have a WMSFactory class.')
if conf.has_option('server', 'debug'):
self.debug = int(conf.get('server', 'debug'))
else:
self.debug = 0
if self.conf.has_option_with_value('server', 'maxage'):
self.max_age = 'max-age=%d' % self.conf.get('server', 'maxage')
else:
self.max_age = None
def __call__(self, apacheReq):
try:
reqparams = util.FieldStorage(apacheReq,keep_blank_values=1)
if not reqparams:
eh = ExceptionHandler130(self.debug)
response = eh.getresponse(reqparams)
apacheReq.content_type = response.content_type
else:
reqparams = lowerparams(reqparams)
port = apacheReq.connection.local_addr[1]
onlineresource = 'http://%s:%s%s?' % (apacheReq.hostname, port, apacheReq.subprocess_env['SCRIPT_NAME'])
if not reqparams.has_key('request'):
raise OGCException('Missing Request parameter.')
request = reqparams['request']
del reqparams['request']
if request == 'GetCapabilities' and not reqparams.has_key('service'):
raise OGCException('Missing service parameter.')
if request in ['GetMap', 'GetFeatureInfo']:
service = 'WMS'
else:
service = reqparams['service']
if reqparams.has_key('service'):
del reqparams['service']
try:
mapnikmodule = __import__('mapnik.ogcserver.' + service)
except:
raise OGCException('Unsupported service "%s".' % service)
ServiceHandlerFactory = getattr(mapnikmodule.ogcserver, service).ServiceHandlerFactory
servicehandler = ServiceHandlerFactory(self.conf, self.mapfactory, onlineresource, reqparams.get('version', None))
if reqparams.has_key('version'):
del reqparams['version']
if request not in servicehandler.SERVICE_PARAMS.keys():
raise OGCException('Operation "%s" not supported.' % request, 'OperationNotSupported')
# Get parameters and pass to WMSFactory in custom "setup" method
ogcparams = servicehandler.processParameters(request, reqparams)
try:
requesthandler = getattr(servicehandler, request)
except:
raise OGCException('Operation "%s" not supported.' % request, 'OperationNotSupported')
response = requesthandler(ogcparams)
apacheReq.content_type = response.content_type
apacheReq.status = apache.HTTP_OK
except Exception, E:
return self.traceback(apacheReq,E)
if self.max_age:
apacheReq.headers_out.add('Cache-Control', max_age)
apacheReq.headers_out.add('Content-Length', str(len(response.content)))
apacheReq.send_http_header()
apacheReq.write(response.content)
return apache.OK
def traceback(self, apacheReq,E):
reqparams = lowerparams(util.FieldStorage(apacheReq))
version = reqparams.get('version', None)
if not version:
version = Version('1.3.0')
else:
version = Version(version)
if version >= '1.3.0':
eh = ExceptionHandler130(self.debug)
else:
eh = ExceptionHandler111(self.debug)
response = eh.getresponse(reqparams)
apacheReq.content_type = response.content_type
apacheReq.headers_out.add('Content-Length', str(len(response.content)))
apacheReq.send_http_header()
apacheReq.write(response.content)
return apache.OK
def lowerparams(params):
reqparams = {}
for key, value in params.items():
reqparams[key.lower()] = value
return reqparams

View file

@ -1,238 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""WMS 1.1.1 compliant GetCapabilities, GetMap, GetFeatureInfo, and Exceptions interface."""
from common import ParameterDefinition, Response, Version, ListFactory, \
ColorFactory, CRSFactory, WMSBaseServiceHandler, CRS, \
BaseExceptionHandler, Projection
from exceptions import OGCException, ServerConfigurationError
from mapnik import Coord
try:
from lxml import etree as ElementTree
except ImportError:
import xml.etree.ElementTree as ElementTree
except ImportError:
import elementtree.ElementTree as ElementTree
class ServiceHandler(WMSBaseServiceHandler):
SERVICE_PARAMS = {
'GetCapabilities': {
'updatesequence': ParameterDefinition(False, str)
},
'GetMap': {
'layers': ParameterDefinition(True, ListFactory(str)),
'styles': ParameterDefinition(True, ListFactory(str)),
'srs': ParameterDefinition(True, CRSFactory(['EPSG'])),
'bbox': ParameterDefinition(True, ListFactory(float)),
'width': ParameterDefinition(True, int),
'height': ParameterDefinition(True, int),
'format': ParameterDefinition(True, str, allowedvalues=('image/png', 'image/jpeg')),
'transparent': ParameterDefinition(False, str, 'FALSE', ('TRUE', 'FALSE')),
'bgcolor': ParameterDefinition(False, ColorFactory, ColorFactory('0xFFFFFF')),
'exceptions': ParameterDefinition(False, str, 'application/vnd.ogc.se_xml', ('application/vnd.ogc.se_xml', 'application/vnd.ogc.se_inimage', 'application/vnd.ogc.se_blank','text/html'))
},
'GetFeatureInfo': {
'layers': ParameterDefinition(True, ListFactory(str)),
'styles': ParameterDefinition(False, ListFactory(str)),
'srs': ParameterDefinition(True, CRSFactory(['EPSG'])),
'bbox': ParameterDefinition(True, ListFactory(float)),
'width': ParameterDefinition(True, int),
'height': ParameterDefinition(True, int),
'format': ParameterDefinition(False, str, allowedvalues=('image/png', 'image/jpeg')),
'transparent': ParameterDefinition(False, str, 'FALSE', ('TRUE', 'FALSE')),
'bgcolor': ParameterDefinition(False, ColorFactory, ColorFactory('0xFFFFFF')),
'exceptions': ParameterDefinition(False, str, 'application/vnd.ogc.se_xml', ('application/vnd.ogc.se_xml', 'application/vnd.ogc.se_inimage', 'application/vnd.ogc.se_blank','text/html')),
'query_layers': ParameterDefinition(True, ListFactory(str)),
'info_format': ParameterDefinition(True, str, allowedvalues=('text/plain', 'text/xml')),
'feature_count': ParameterDefinition(False, int, 1),
'x': ParameterDefinition(True, int),
'y': ParameterDefinition(True, int)
}
}
CONF_SERVICE = [
['title', 'Title', str],
['abstract', 'Abstract', str],
['onlineresource', 'OnlineResource', str],
['fees', 'Fees', str],
['accessconstraints', 'AccessConstraints', str],
['keywordlist', 'KeywordList', str]
]
capabilitiesxmltemplate = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://www.digitalearth.gov/wmt/xml/capabilities_1_1_1.dtd">
<WMT_MS_Capabilities version="1.1.1" updateSequence="0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.opengis.net/wms">
<Service>
<Name>WMS</Name>
</Service>
<Capability>
<Request>
<GetCapabilities>
<Format>application/vnd.ogc.wms_xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple"/>
</Get>
</HTTP>
</DCPType>
</GetCapabilities>
<GetMap>
<Format>image/png</Format>
<Format>image/jpeg</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple"/>
</Get>
</HTTP>
</DCPType>
</GetMap>
<GetFeatureInfo>
<Format>text/plain</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple"/>
</Get>
</HTTP>
</DCPType>
</GetFeatureInfo>
</Request>
<Exception>
<Format>application/vnd.ogc.se_xml</Format>
<Format>application/vnd.ogc.se_inimage</Format>
<Format>application/vnd.ogc.se_blank</Format>
</Exception>
<Layer>
<Title>A Mapnik WMS Server</Title>
<Abstract>A Mapnik WMS Server</Abstract>
</Layer>
</Capability>
</WMT_MS_Capabilities>
"""
def __init__(self, conf, mapfactory, opsonlineresource):
self.conf = conf
self.mapfactory = mapfactory
self.opsonlineresource = opsonlineresource
if self.conf.has_option('service', 'allowedepsgcodes'):
self.allowedepsgcodes = map(lambda code: 'epsg:%s' % code, self.conf.get('service', 'allowedepsgcodes').split(','))
else:
raise ServerConfigurationError('Allowed EPSG codes not properly configured.')
self.capabilities = None
def GetCapabilities(self, params):
if not self.capabilities:
capetree = ElementTree.fromstring(self.capabilitiesxmltemplate)
elements = capetree.findall('Capability//OnlineResource')
for element in elements:
element.set('{http://www.w3.org/1999/xlink}href', self.opsonlineresource)
self.processServiceCapabilities(capetree)
rootlayerelem = capetree.find('{http://www.opengis.net/wms}Capability/{http://www.opengis.net/wms}Layer')
for epsgcode in self.allowedepsgcodes:
rootlayercrs = ElementTree.Element('SRS')
rootlayercrs.text = epsgcode.upper()
rootlayerelem.append(rootlayercrs)
for layer in self.mapfactory.ordered_layers:
layerproj = Projection(layer.srs)
layername = ElementTree.Element('Name')
layername.text = layer.name
env = layer.envelope()
llp = layerproj.inverse(Coord(env.minx, env.miny))
urp = layerproj.inverse(Coord(env.maxx, env.maxy))
latlonbb = ElementTree.Element('LatLonBoundingBox')
latlonbb.set('minx', str(llp.x))
latlonbb.set('miny', str(llp.y))
latlonbb.set('maxx', str(urp.x))
latlonbb.set('maxy', str(urp.y))
layerbbox = ElementTree.Element('BoundingBox')
layerbbox.set('SRS', layerproj.epsgstring())
layerbbox.set('minx', str(env.minx))
layerbbox.set('miny', str(env.miny))
layerbbox.set('maxx', str(env.maxx))
layerbbox.set('maxy', str(env.maxy))
layere = ElementTree.Element('Layer')
layere.append(layername)
if layer.title:
layertitle = ElementTree.Element('Title')
layertitle.text = layer.title
layere.append(layertitle)
if layer.abstract:
layerabstract = ElementTree.Element('Abstract')
layerabstract.text = layer.abstract
layere.append(layerabstract)
if layer.queryable:
layere.set('queryable', '1')
layere.append(latlonbb)
layere.append(layerbbox)
if len(layer.wmsextrastyles) > 0:
for extrastyle in [layer.wmsdefaultstyle] + list(layer.wmsextrastyles):
style = ElementTree.Element('Style')
stylename = ElementTree.Element('Name')
stylename.text = extrastyle
styletitle = ElementTree.Element('Title')
styletitle.text = extrastyle
style.append(stylename)
style.append(styletitle)
layere.append(style)
rootlayerelem.append(layere)
self.capabilities = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n' + ElementTree.tostring(capetree)
response = Response('application/vnd.ogc.wms_xml', self.capabilities)
return response
def GetMap(self, params):
params['crs'] = params['srs']
return WMSBaseServiceHandler.GetMap(self, params)
def GetFeatureInfo(self, params):
params['crs'] = params['srs']
params['i'] = params['x']
params['j'] = params['y']
return WMSBaseServiceHandler.GetFeatureInfo(self, params, 'query_map_point')
class ExceptionHandler(BaseExceptionHandler):
xmlmimetype = "application/vnd.ogc.se_xml"
xmltemplate = ElementTree.fromstring("""<?xml version='1.0' encoding="UTF-8" standalone="no"?>
<!DOCTYPE ServiceExceptionReport SYSTEM "http://www.digitalearth.gov/wmt/xml/exception_1_1_1.dtd">
<ServiceExceptionReport version="1.1.1">
<ServiceException />
</ServiceExceptionReport>
""")
xpath = 'ServiceException'
handlers = {'application/vnd.ogc.se_xml': BaseExceptionHandler.xmlhandler,
'application/vnd.ogc.se_inimage': BaseExceptionHandler.inimagehandler,
'application/vnd.ogc.se_blank': BaseExceptionHandler.blankhandler,
'text/html': BaseExceptionHandler.htmlhandler}
defaulthandler = 'application/vnd.ogc.se_xml'

View file

@ -1,264 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""WMS 1.3.0 compliant GetCapabilities, GetMap, GetFeatureInfo, and Exceptions interface."""
from common import ParameterDefinition, Response, Version, ListFactory, \
ColorFactory, CRSFactory, CRS, WMSBaseServiceHandler, \
BaseExceptionHandler, Projection, Box2d
from exceptions import OGCException, ServerConfigurationError
from mapnik import Coord
try:
from lxml import etree as ElementTree
except ImportError:
import xml.etree.ElementTree as ElementTree
except ImportError:
import elementtree.ElementTree as ElementTree
class ServiceHandler(WMSBaseServiceHandler):
SERVICE_PARAMS = {
'GetCapabilities': {
'format': ParameterDefinition(False, str, 'text/xml', ('text/xml',), True),
'updatesequence': ParameterDefinition(False, str)
},
'GetMap': {
'layers': ParameterDefinition(True, ListFactory(str)),
'styles': ParameterDefinition(True, ListFactory(str)),
'crs': ParameterDefinition(True, CRSFactory(['EPSG'])),
'bbox': ParameterDefinition(True, ListFactory(float)),
'width': ParameterDefinition(True, int),
'height': ParameterDefinition(True, int),
'format': ParameterDefinition(True, str, allowedvalues=('image/png', 'image/jpeg')),
'transparent': ParameterDefinition(False, str, 'FALSE', ('TRUE', 'FALSE')),
'bgcolor': ParameterDefinition(False, ColorFactory, ColorFactory('0xFFFFFF')),
'exceptions': ParameterDefinition(False, str, 'XML', ('XML', 'INIMAGE', 'BLANK','HTML')),
},
'GetFeatureInfo': {
'layers': ParameterDefinition(True, ListFactory(str)),
'styles': ParameterDefinition(False, ListFactory(str)),
'crs': ParameterDefinition(True, CRSFactory(['EPSG'])),
'bbox': ParameterDefinition(True, ListFactory(float)),
'width': ParameterDefinition(True, int),
'height': ParameterDefinition(True, int),
'format': ParameterDefinition(False, str, allowedvalues=('image/png', 'image/jpeg')),
'transparent': ParameterDefinition(False, str, 'FALSE', ('TRUE', 'FALSE')),
'bgcolor': ParameterDefinition(False, ColorFactory, ColorFactory('0xFFFFFF')),
'exceptions': ParameterDefinition(False, str, 'XML', ('XML', 'INIMAGE', 'BLANK','HTML')),
'query_layers': ParameterDefinition(True, ListFactory(str)),
'info_format': ParameterDefinition(True, str, allowedvalues=('text/plain', 'text/xml')),
'feature_count': ParameterDefinition(False, int, 1),
'i': ParameterDefinition(True, float),
'j': ParameterDefinition(True, float)
}
}
CONF_SERVICE = [
['title', 'Title', str],
['abstract', 'Abstract', str],
['onlineresource', 'OnlineResource', str],
['fees', 'Fees', str],
['accessconstraints', 'AccessConstraints', str],
['layerlimit', 'LayerLimit', int],
['maxwidth', 'MaxWidth', int],
['maxheight', 'MaxHeight', int],
['keywordlist', 'KeywordList', str]
]
capabilitiesxmltemplate = """<?xml version="1.0" encoding="UTF-8"?>
<WMS_Capabilities version="1.3.0" xmlns="http://www.opengis.net/wms"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd">
<Service>
<Name>WMS</Name>
</Service>
<Capability>
<Request>
<GetCapabilities>
<Format>text/xml</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple"/>
</Get>
</HTTP>
</DCPType>
</GetCapabilities>
<GetMap>
<Format>image/png</Format>
<Format>image/jpeg</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple"/>
</Get>
</HTTP>
</DCPType>
</GetMap>
<GetFeatureInfo>
<Format>text/plain</Format>
<DCPType>
<HTTP>
<Get>
<OnlineResource xlink:type="simple"/>
</Get>
</HTTP>
</DCPType>
</GetFeatureInfo>
</Request>
<Exception>
<Format>XML</Format>
<Format>INIMAGE</Format>
<Format>BLANK</Format>
</Exception>
<Layer>
<Title>A Mapnik WMS Server</Title>
<Abstract>A Mapnik WMS Server</Abstract>
</Layer>
</Capability>
</WMS_Capabilities>
"""
def __init__(self, conf, mapfactory, opsonlineresource):
self.conf = conf
self.mapfactory = mapfactory
self.opsonlineresource = opsonlineresource
if self.conf.has_option('service', 'allowedepsgcodes'):
self.allowedepsgcodes = map(lambda code: 'epsg:%s' % code, self.conf.get('service', 'allowedepsgcodes').split(','))
else:
raise ServerConfigurationError('Allowed EPSG codes not properly configured.')
self.capabilities = None
def GetCapabilities(self, params):
if not self.capabilities:
capetree = ElementTree.fromstring(self.capabilitiesxmltemplate)
elements = capetree.findall('{http://www.opengis.net/wms}Capability//{http://www.opengis.net/wms}OnlineResource')
for element in elements:
element.set('{http://www.w3.org/1999/xlink}href', self.opsonlineresource)
self.processServiceCapabilities(capetree)
rootlayerelem = capetree.find('{http://www.opengis.net/wms}Capability/{http://www.opengis.net/wms}Layer')
for epsgcode in self.allowedepsgcodes:
rootlayercrs = ElementTree.Element('CRS')
rootlayercrs.text = epsgcode.upper()
rootlayerelem.append(rootlayercrs)
for layer in self.mapfactory.ordered_layers:
layerproj = Projection(layer.srs)
layername = ElementTree.Element('Name')
layername.text = layer.name
env = layer.envelope()
layerexgbb = ElementTree.Element('EX_GeographicBoundingBox')
ll = layerproj.inverse(Coord(env.minx, env.miny))
ur = layerproj.inverse(Coord(env.maxx, env.maxy))
exgbb_wbl = ElementTree.Element('westBoundLongitude')
exgbb_wbl.text = str(ll.x)
layerexgbb.append(exgbb_wbl)
exgbb_ebl = ElementTree.Element('eastBoundLongitude')
exgbb_ebl.text = str(ur.x)
layerexgbb.append(exgbb_ebl)
exgbb_sbl = ElementTree.Element('southBoundLatitude')
exgbb_sbl.text = str(ll.y)
layerexgbb.append(exgbb_sbl)
exgbb_nbl = ElementTree.Element('northBoundLatitude')
exgbb_nbl.text = str(ur.y)
layerexgbb.append(exgbb_nbl)
layerbbox = ElementTree.Element('BoundingBox')
layerbbox.set('CRS', layerproj.epsgstring())
layerbbox.set('minx', str(env.minx))
layerbbox.set('miny', str(env.miny))
layerbbox.set('maxx', str(env.maxx))
layerbbox.set('maxy', str(env.maxy))
layere = ElementTree.Element('Layer')
layere.append(layername)
if layer.title:
layertitle = ElementTree.Element('Title')
layertitle.text = layer.title
layere.append(layertitle)
if layer.abstract:
layerabstract = ElementTree.Element('Abstract')
layerabstract.text = layer.abstract
layere.append(layerabstract)
if layer.queryable:
layere.set('queryable', '1')
layere.append(layerexgbb)
layere.append(layerbbox)
if len(layer.wmsextrastyles) > 0:
for extrastyle in [layer.wmsdefaultstyle] + list(layer.wmsextrastyles):
style = ElementTree.Element('Style')
stylename = ElementTree.Element('Name')
stylename.text = extrastyle
styletitle = ElementTree.Element('Title')
styletitle.text = extrastyle
style.append(stylename)
style.append(styletitle)
layere.append(style)
rootlayerelem.append(layere)
self.capabilities = '<?xml version="1.0" encoding="UTF-8"?>' + ElementTree.tostring(capetree)
response = Response('text/xml', self.capabilities)
return response
def GetMap(self, params):
if params['width'] > int(self.conf.get('service', 'maxwidth')) or params['height'] > int(self.conf.get('service', 'maxheight')):
raise OGCException('Requested map size exceeds limits set by this server.')
return WMSBaseServiceHandler.GetMap(self, params)
def _buildMap(self, params):
""" Override _buildMap method to handle reverse axis ordering in WMS 1.3.0.
More info: http://mapserver.org/development/rfc/ms-rfc-30.html
'when using epsg code >=4000 and <5000 will be assumed to have a reversed axes.'
"""
# Call superclass method
m = WMSBaseServiceHandler._buildMap(self, params)
# for range of epsg codes reverse axis
if params['crs'].code >= 4000 and params['crs'].code < 5000:
m.zoom_to_box(Box2d(params['bbox'][1], params['bbox'][0], params['bbox'][3], params['bbox'][2]))
return m
class ExceptionHandler(BaseExceptionHandler):
xmlmimetype = "text/xml"
xmltemplate = ElementTree.fromstring("""<?xml version='1.0' encoding="UTF-8"?>
<ServiceExceptionReport version="1.3.0"
xmlns="http://www.opengis.net/ogc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/ogc http://schemas.opengis.net/wms/1.3.0/exceptions_1_3_0.xsd">
<ServiceException/>
</ServiceExceptionReport>
""")
xpath = '{http://www.opengis.net/ogc}ServiceException'
handlers = {'XML': BaseExceptionHandler.xmlhandler,
'INIMAGE': BaseExceptionHandler.inimagehandler,
'BLANK': BaseExceptionHandler.blankhandler,
'HTML': BaseExceptionHandler.htmlhandler}
defaulthandler = 'XML'

View file

@ -1,106 +0,0 @@
#
# This file is part of Mapnik (c++ mapping toolkit)
#
# Copyright (C) 2006 Jean-Francois Doyon
#
# Mapnik is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
"""WSGI application wrapper for Mapnik OGC WMS Server."""
from exceptions import OGCException, ServerConfigurationError
from configparser import SafeConfigParser
from cgi import parse_qs
from wms111 import ExceptionHandler as ExceptionHandler111
from wms130 import ExceptionHandler as ExceptionHandler130
from common import Version
class WSGIApp:
def __init__(self, configpath):
conf = SafeConfigParser()
conf.readfp(open(configpath))
self.conf = conf
if not conf.has_option_with_value('server', 'module'):
raise ServerConfigurationError('The factory module is not defined in the configuration file.')
try:
mapfactorymodule = __import__(conf.get('server', 'module'))
except ImportError:
raise ServerConfigurationError('The factory module could not be loaded.')
if hasattr(mapfactorymodule, 'WMSFactory'):
self.mapfactory = getattr(mapfactorymodule, 'WMSFactory')()
else:
raise ServerConfigurationError('The factory module does not have a WMSFactory class.')
if conf.has_option('server', 'debug'):
self.debug = int(conf.get('server', 'debug'))
else:
self.debug = 0
if self.conf.has_option_with_value('server', 'maxage'):
self.max_age = 'max-age=%d' % self.conf.get('server', 'maxage')
else:
self.max_age = None
def __call__(self, environ, start_response):
reqparams = {}
for key, value in parse_qs(environ['QUERY_STRING'], True).items():
reqparams[key.lower()] = value[0]
onlineresource = 'http://%s:%s%s?' % (environ['SERVER_NAME'], environ['SERVER_PORT'], environ['PATH_INFO'])
try:
if not reqparams.has_key('request'):
raise OGCException('Missing request parameter.')
request = reqparams['request']
del reqparams['request']
if request == 'GetCapabilities' and not reqparams.has_key('service'):
raise OGCException('Missing service parameter.')
if request in ['GetMap', 'GetFeatureInfo']:
service = 'WMS'
else:
service = reqparams['service']
if reqparams.has_key('service'):
del reqparams['service']
try:
mapnikmodule = __import__('mapnik.ogcserver.' + service)
except:
raise OGCException('Unsupported service "%s".' % service)
ServiceHandlerFactory = getattr(mapnikmodule.ogcserver, service).ServiceHandlerFactory
servicehandler = ServiceHandlerFactory(self.conf, self.mapfactory, onlineresource, reqparams.get('version', None))
if reqparams.has_key('version'):
del reqparams['version']
if request not in servicehandler.SERVICE_PARAMS.keys():
raise OGCException('Operation "%s" not supported.' % request, 'OperationNotSupported')
ogcparams = servicehandler.processParameters(request, reqparams)
try:
requesthandler = getattr(servicehandler, request)
except:
raise OGCException('Operation "%s" not supported.' % request, 'OperationNotSupported')
response = requesthandler(ogcparams)
except:
version = reqparams.get('version', None)
if not version:
version = Version('1.3.0')
else:
version = Version(version)
if version >= '1.3.0':
eh = ExceptionHandler130(self.debug)
else:
eh = ExceptionHandler111(self.debug)
response = eh.getresponse(reqparams)
response_headers = [('Content-Type', response.content_type),('Content-Length', str(len(response.content)))]
if self.max_age:
response_headers.append(('Cache-Control', max_age))
start_response('200 OK', response_headers)
yield response.content