diff --git a/bindings/python/mapnik/ogcserver/WMS.py b/bindings/python/mapnik/ogcserver/WMS.py deleted file mode 100644 index fefb66d46..000000000 --- a/bindings/python/mapnik/ogcserver/WMS.py +++ /dev/null @@ -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)) diff --git a/bindings/python/mapnik/ogcserver/__init__.py b/bindings/python/mapnik/ogcserver/__init__.py deleted file mode 100644 index 45456fd00..000000000 --- a/bindings/python/mapnik/ogcserver/__init__.py +++ /dev/null @@ -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) - diff --git a/bindings/python/mapnik/ogcserver/cgiserver.py b/bindings/python/mapnik/ogcserver/cgiserver.py deleted file mode 100644 index 3acbebbfd..000000000 --- a/bindings/python/mapnik/ogcserver/cgiserver.py +++ /dev/null @@ -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 diff --git a/bindings/python/mapnik/ogcserver/common.py b/bindings/python/mapnik/ogcserver/common.py deleted file mode 100644 index c55a042f2..000000000 --- a/bindings/python/mapnik/ogcserver/common.py +++ /dev/null @@ -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 = ''' -

Welcome to the Mapnik OGCServer.

-

Ready to accept map requests...

-

For more info see: trac.mapnik.org

- ''' - 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 = '

OGCServer Error:

%s
\n

Traceback:

%s
\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 = """ - - - """ - - 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 '\n' + ElementTree.tostring(self.rootelement) diff --git a/bindings/python/mapnik/ogcserver/configparser.py b/bindings/python/mapnik/ogcserver/configparser.py deleted file mode 100644 index 99788412d..000000000 --- a/bindings/python/mapnik/ogcserver/configparser.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/bindings/python/mapnik/ogcserver/exceptions.py b/bindings/python/mapnik/ogcserver/exceptions.py deleted file mode 100644 index 0ef7dcaf0..000000000 --- a/bindings/python/mapnik/ogcserver/exceptions.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/bindings/python/mapnik/ogcserver/modserver.py b/bindings/python/mapnik/ogcserver/modserver.py deleted file mode 100644 index 22a260aee..000000000 --- a/bindings/python/mapnik/ogcserver/modserver.py +++ /dev/null @@ -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 diff --git a/bindings/python/mapnik/ogcserver/wms111.py b/bindings/python/mapnik/ogcserver/wms111.py deleted file mode 100644 index 62d44ec2d..000000000 --- a/bindings/python/mapnik/ogcserver/wms111.py +++ /dev/null @@ -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 = """ - - - - WMS - - - - - application/vnd.ogc.wms_xml - - - - - - - - - - image/png - image/jpeg - - - - - - - - - - text/plain - - - - - - - - - - - application/vnd.ogc.se_xml - application/vnd.ogc.se_inimage - application/vnd.ogc.se_blank - - - A Mapnik WMS Server - A Mapnik WMS Server - - - - """ - - 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 = '\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(""" - - - - - """) - - 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' diff --git a/bindings/python/mapnik/ogcserver/wms130.py b/bindings/python/mapnik/ogcserver/wms130.py deleted file mode 100644 index 609da66b3..000000000 --- a/bindings/python/mapnik/ogcserver/wms130.py +++ /dev/null @@ -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 = """ - - - WMS - - - - - text/xml - - - - - - - - - - image/png - image/jpeg - - - - - - - - - - text/plain - - - - - - - - - - - XML - INIMAGE - BLANK - - - A Mapnik WMS Server - A Mapnik WMS Server - - - - """ - - 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 = '' + 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(""" - - - - """) - - xpath = '{http://www.opengis.net/ogc}ServiceException' - - handlers = {'XML': BaseExceptionHandler.xmlhandler, - 'INIMAGE': BaseExceptionHandler.inimagehandler, - 'BLANK': BaseExceptionHandler.blankhandler, - 'HTML': BaseExceptionHandler.htmlhandler} - - defaulthandler = 'XML' diff --git a/bindings/python/mapnik/ogcserver/wsgi.py b/bindings/python/mapnik/ogcserver/wsgi.py deleted file mode 100644 index 66ba6ed10..000000000 --- a/bindings/python/mapnik/ogcserver/wsgi.py +++ /dev/null @@ -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 -