OGCServer now lives at https://github.com/mapnik/OGCServer, remove from python bindings
This commit is contained in:
parent
14700dba16
commit
d7ac269a6c
10 changed files with 0 additions and 1621 deletions
|
@ -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))
|
|
@ -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)
|
||||
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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'
|
|
@ -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
|
||||
|
Loading…
Reference in a new issue