From da9640c39d5e4b56707fa21b24b5ba1f73696a22 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Thu, 12 Feb 2009 01:17:30 +0000 Subject: [PATCH] convert to unix line endings --- bindings/python/mapnik/ogcserver/common.py | 918 +++++++++--------- .../python/mapnik/ogcserver/exceptions.py | 50 +- bindings/python/mapnik/ogcserver/wsgi.py | 192 ++-- 3 files changed, 580 insertions(+), 580 deletions(-) diff --git a/bindings/python/mapnik/ogcserver/common.py b/bindings/python/mapnik/ogcserver/common.py index 2fbd41104..0eb9b9da7 100644 --- a/bindings/python/mapnik/ogcserver/common.py +++ b/bindings/python/mapnik/ogcserver/common.py @@ -1,459 +1,459 @@ -# -# 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$ - -from exceptions import OGCException, ServerConfigurationError -from mapnik import Map, Color, Envelope, render, Image, Projection as MapnikProjection, render_to_file, Coord -from PIL.Image import fromstring, 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 -from lxml import etree as ElementTree -import re -import sys -# 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') - -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() - for layerindex, layername in enumerate(params['query_layers']): - if layername in params['layers']: - if m.layers[layerindex].queryable: - features = getattr(m, querymethodname)(layerindex, params['i'], params['j']) - if features: - writer.addlayer(m.layers[layerindex].name) - for feature in features: - writer.addfeature() - for prop in feature.properties: - writer.addattribute(prop.key(), prop.data()) - 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': - m.background = params['bgcolor'] - else: - m.background = Color(0, 0, 0, 0) - maplayers = self.mapfactory.layers - mapstyles = self.mapfactory.styles - mapaggregatestyles = self.mapfactory.aggregatestyles - for layerindex, layername in enumerate(params['layers']): - try: - layer = maplayers[layername] - except KeyError: - raise OGCException('Layer "%s" not defined.' % layername, 'LayerNotDefined') - reqstyle = params['styles'][layerindex] - 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(Envelope(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' - 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 not exceptions or not self.handlers.has_key(exceptions): - exceptions = self.defaulthandler - return self.handlers[exceptions](self, code, message, params) - - 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 += '[%s]\n' % name - - def addfeature(self): - 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 = value.unicode() - attribute.append(attname) - attribute.append(attvalue) - self.currentfeature.append(attribute) - - def __str__(self): - return '\n' + ElementTree.tostring(self.rootelement) +# +# 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$ + +from exceptions import OGCException, ServerConfigurationError +from mapnik import Map, Color, Envelope, render, Image, Projection as MapnikProjection, render_to_file, Coord +from PIL.Image import fromstring, 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 +from lxml import etree as ElementTree +import re +import sys +# 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') + +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() + for layerindex, layername in enumerate(params['query_layers']): + if layername in params['layers']: + if m.layers[layerindex].queryable: + features = getattr(m, querymethodname)(layerindex, params['i'], params['j']) + if features: + writer.addlayer(m.layers[layerindex].name) + for feature in features: + writer.addfeature() + for prop in feature.properties: + writer.addattribute(prop.key(), prop.data()) + 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': + m.background = params['bgcolor'] + else: + m.background = Color(0, 0, 0, 0) + maplayers = self.mapfactory.layers + mapstyles = self.mapfactory.styles + mapaggregatestyles = self.mapfactory.aggregatestyles + for layerindex, layername in enumerate(params['layers']): + try: + layer = maplayers[layername] + except KeyError: + raise OGCException('Layer "%s" not defined.' % layername, 'LayerNotDefined') + reqstyle = params['styles'][layerindex] + 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(Envelope(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' + 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 not exceptions or not self.handlers.has_key(exceptions): + exceptions = self.defaulthandler + return self.handlers[exceptions](self, code, message, params) + + 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 += '[%s]\n' % name + + def addfeature(self): + 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 = value.unicode() + 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/exceptions.py b/bindings/python/mapnik/ogcserver/exceptions.py index 215de33fd..c814903d4 100644 --- a/bindings/python/mapnik/ogcserver/exceptions.py +++ b/bindings/python/mapnik/ogcserver/exceptions.py @@ -1,26 +1,26 @@ -# -# 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$ - -class OGCException(Exception): - pass - -class ServerConfigurationError(Exception): +# +# 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$ + +class OGCException(Exception): + pass + +class ServerConfigurationError(Exception): pass \ No newline at end of file diff --git a/bindings/python/mapnik/ogcserver/wsgi.py b/bindings/python/mapnik/ogcserver/wsgi.py index dd4ee56c8..089fde653 100644 --- a/bindings/python/mapnik/ogcserver/wsgi.py +++ b/bindings/python/mapnik/ogcserver/wsgi.py @@ -1,97 +1,97 @@ -# -# 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$ - -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 - - 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['SCRIPT_NAME']) - 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) - start_response('200 OK', [('Content-Type', response.content_type)]) - yield response.content +# +# 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$ + +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 + + 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['SCRIPT_NAME']) + 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) + start_response('200 OK', [('Content-Type', response.content_type)]) + yield response.content \ No newline at end of file