OK, final fixes to the WMS stuff, cleaned up documentation, and so on.
Things should be good to go for a first try by the wider community!
This commit is contained in:
parent
af55c99fa4
commit
b8ac9b1984
9 changed files with 193 additions and 60 deletions
36
INSTALL
36
INSTALL
|
@ -22,7 +22,8 @@ First, here is a quick list of the software dependencies:
|
||||||
- libtiff
|
- libtiff
|
||||||
- libz
|
- libz
|
||||||
- libfreetype2
|
- libfreetype2
|
||||||
- (Optional) PostgreSQL libraries
|
- (Optional) PostgreSQL libraries (For PostGIS support)
|
||||||
|
- (Optional) PROJ.4 (More on this below)
|
||||||
|
|
||||||
- Python 1.5.2 or greater to build Mapnik
|
- Python 1.5.2 or greater to build Mapnik
|
||||||
- (Optional) Python 2.2 or greater for the Python language bindings
|
- (Optional) Python 2.2 or greater for the Python language bindings
|
||||||
|
@ -33,6 +34,7 @@ If your system does NOT have one of these installed, you will need to install th
|
||||||
|
|
||||||
Also, a minimum of 256MB of RAM is recommended for the build process.
|
Also, a minimum of 256MB of RAM is recommended for the build process.
|
||||||
|
|
||||||
|
|
||||||
Building
|
Building
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -101,6 +103,14 @@ PGSQL_LIBS: Search path for PostgreSQL library files ( /path/to/PGSQL_LIBS )
|
||||||
default: /usr/lib
|
default: /usr/lib
|
||||||
actual: /usr/lib
|
actual: /usr/lib
|
||||||
|
|
||||||
|
PROJ_INCLUDES: Search path for PROJ.4 include files ( /path/to/PROJ_INCLUDES )
|
||||||
|
default: /usr/local/include
|
||||||
|
actual: /usr/local/include
|
||||||
|
|
||||||
|
PROJ_LIBS: Search path for PROJ.4 include files ( /path/to/PROJ_LIBS )
|
||||||
|
default: /usr/local/lib
|
||||||
|
actual: /usr/local/lib
|
||||||
|
|
||||||
PYTHON: Python executable ( /path/to/PYTHON )
|
PYTHON: Python executable ( /path/to/PYTHON )
|
||||||
default: /usr/bin/python
|
default: /usr/bin/python
|
||||||
actual: /usr/bin/python
|
actual: /usr/bin/python
|
||||||
|
@ -146,6 +156,30 @@ $PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages/mapnik: Python bindings
|
||||||
If you're using the default PREFIX, you will most likely need to be root to perform the install.
|
If you're using the default PREFIX, you will most likely need to be root to perform the install.
|
||||||
|
|
||||||
|
|
||||||
|
A note on projection support
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
At this time Mapnik's core C++ library and map rendering engine does NOT support on-the-fly cartographic
|
||||||
|
reprojections.
|
||||||
|
|
||||||
|
Mapnik can however be configured to build the Python API to the PROJ.4 library. This provides projection
|
||||||
|
support through Python, and is used by the WMS ogcserver feature, since that server is written in Python.
|
||||||
|
|
||||||
|
Here is an example on how to use it:
|
||||||
|
|
||||||
|
>>> from mapnik import Projection
|
||||||
|
registered datasource : raster
|
||||||
|
registered datasource : shape
|
||||||
|
registered datasource : postgis
|
||||||
|
>>> p = Projection(['init=epsg:42304'])
|
||||||
|
>>> p.Inverse(12345.245,143225.56)
|
||||||
|
[-94.825927695613018, 50.290732340975467]
|
||||||
|
>>>
|
||||||
|
|
||||||
|
The Projection() instance provides Inverse() and Forward() methods. For details on the possible parameters,
|
||||||
|
see the PROJ.4 documentation.
|
||||||
|
|
||||||
|
|
||||||
Test
|
Test
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
53
bindings/python/mapnik/ogcserver/WMS.py
Normal file
53
bindings/python/mapnik/ogcserver/WMS.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#
|
||||||
|
# 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 common import Version
|
||||||
|
from exceptions import OGCException, ServerConfigurationError
|
||||||
|
from wms111 import ServiceHandler as ServiceHandler111
|
||||||
|
from wms130 import ServiceHandler as ServiceHandler130
|
||||||
|
|
||||||
|
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.styles = {}
|
||||||
|
|
||||||
|
def register_layer(self, layer):
|
||||||
|
layername = layer.name()
|
||||||
|
if not layername:
|
||||||
|
raise ServerConfigurationError('There is an un-named layer.')
|
||||||
|
self.layers[layername] = layer
|
||||||
|
|
||||||
|
def register_style(self, name, style):
|
||||||
|
if not name:
|
||||||
|
raise ServerConfigurationError('There is an un-named style.')
|
||||||
|
self.styles[name] = style
|
|
@ -25,10 +25,11 @@ environ['PYTHON_EGG_CACHE'] = gettempdir()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from jon import cgi
|
from jon import cgi
|
||||||
from exceptions import OGCException
|
from exceptions import OGCException, ServerConfigurationError
|
||||||
from wms130 import ExceptionHandler
|
from wms111 import ExceptionHandler as ExceptionHandler111
|
||||||
from lxml import etree as ElementTree
|
from wms130 import ExceptionHandler as ExceptionHandler130
|
||||||
from ConfigParser import SafeConfigParser
|
from ConfigParser import SafeConfigParser
|
||||||
|
from common import Version
|
||||||
|
|
||||||
class Handler(cgi.DebugHandler):
|
class Handler(cgi.DebugHandler):
|
||||||
|
|
||||||
|
@ -36,29 +37,36 @@ class Handler(cgi.DebugHandler):
|
||||||
conf = SafeConfigParser()
|
conf = SafeConfigParser()
|
||||||
conf.readfp(open(self.configpath))
|
conf.readfp(open(self.configpath))
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
mapfactorymodule = __import__(conf.get('server', 'module'))
|
if not conf.has_option('server', 'module'):
|
||||||
self.mapfactory = getattr(mapfactorymodule, 'WMSFactory')()
|
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):
|
def process(self, req):
|
||||||
exceptionhandler = ExceptionHandler
|
reqparams = lowerparams(req.params)
|
||||||
reqparams = {}
|
|
||||||
for key, value in req.params.items():
|
|
||||||
reqparams[key.lower()] = value
|
|
||||||
onlineresource = 'http://%s:%s%s?' % (req.environ['SERVER_NAME'], req.environ['SERVER_PORT'], req.environ['SCRIPT_NAME'])
|
onlineresource = 'http://%s:%s%s?' % (req.environ['SERVER_NAME'], req.environ['SERVER_PORT'], req.environ['SCRIPT_NAME'])
|
||||||
# try:
|
|
||||||
if not reqparams.has_key('request'):
|
if not reqparams.has_key('request'):
|
||||||
raise OGCException('Missing request parameter.')
|
raise OGCException('Missing request parameter.')
|
||||||
if reqparams['request'] == 'GetCapabilities' and not reqparams.has_key('service'):
|
if reqparams['request'] == 'GetCapabilities' and not reqparams.has_key('service'):
|
||||||
raise OGCException('Missing service parameter.')
|
raise OGCException('Missing service parameter.')
|
||||||
if reqparams['request'] in ['GetMap', 'GetFeatureInfo']:
|
if reqparams['request'] in ['GetMap', 'GetFeatureInfo']:
|
||||||
reqparams['service'] = 'wms'
|
reqparams['service'] = 'WMS'
|
||||||
service = reqparams['service'].lower()
|
|
||||||
try:
|
try:
|
||||||
mapnikmodule = __import__('mapnik.ogcserver.' + service)
|
mapnikmodule = __import__('mapnik.ogcserver.' + reqparams['service'])
|
||||||
except:
|
except:
|
||||||
raise OGCException('Unsupported service "%s".' % service)
|
raise OGCException('Unsupported service "%s".' % reqparams['service'])
|
||||||
ServiceHandlerFactory = getattr(mapnikmodule.ogcserver, service).ServiceHandlerFactory
|
ServiceHandlerFactory = getattr(mapnikmodule.ogcserver, reqparams['service']).ServiceHandlerFactory
|
||||||
servicehandler, exceptionhandler = ServiceHandlerFactory(self.conf, self.mapfactory, onlineresource, reqparams.get('version', None))
|
servicehandler = ServiceHandlerFactory(self.conf, self.mapfactory, onlineresource, reqparams.get('version', None))
|
||||||
if reqparams['request'] not in servicehandler.SERVICE_PARAMS.keys():
|
if reqparams['request'] not in servicehandler.SERVICE_PARAMS.keys():
|
||||||
raise OGCException('Operation "%s" not supported.' % reqparams['request'], 'OperationNotSupported')
|
raise OGCException('Operation "%s" not supported.' % reqparams['request'], 'OperationNotSupported')
|
||||||
ogcparams = servicehandler.processParameters(reqparams['request'], reqparams)
|
ogcparams = servicehandler.processParameters(reqparams['request'], reqparams)
|
||||||
|
@ -67,14 +75,25 @@ class Handler(cgi.DebugHandler):
|
||||||
except:
|
except:
|
||||||
raise OGCException('Operation "%s" not supported.' % reqparams['request'], 'OperationNotSupported')
|
raise OGCException('Operation "%s" not supported.' % reqparams['request'], 'OperationNotSupported')
|
||||||
response = requesthandler(ogcparams)
|
response = requesthandler(ogcparams)
|
||||||
# except:
|
|
||||||
# raise
|
|
||||||
# else:
|
|
||||||
req.set_header('Content-Type', response.content_type)
|
req.set_header('Content-Type', response.content_type)
|
||||||
req.write(response.content)
|
req.write(response.content)
|
||||||
"""
|
|
||||||
except OGCException:
|
def traceback(self, req):
|
||||||
eh = exceptionhandler()
|
reqparams = lowerparams(req.params)
|
||||||
req.set_header('Content-Type', eh.mimetype)
|
version = reqparams.get('version', None)
|
||||||
req.write(ElementTree.tostring(eh.getexcetree(sys.exc_info()[1])))
|
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)
|
||||||
|
req.set_header('Content-Type', eh.mimetype)
|
||||||
|
req.write(eh.getcontent())
|
||||||
|
|
||||||
|
def lowerparams(params):
|
||||||
|
reqparams = {}
|
||||||
|
for key, value in params.items():
|
||||||
|
reqparams[key.lower()] = value
|
||||||
|
return reqparams
|
|
@ -195,8 +195,6 @@ class CRSFactory:
|
||||||
class WMSBaseServiceHandler(BaseServiceHandler):
|
class WMSBaseServiceHandler(BaseServiceHandler):
|
||||||
|
|
||||||
def GetMap(self, params):
|
def GetMap(self, params):
|
||||||
if str(params['crs']) != str(self.crs):
|
|
||||||
raise OGCException('Unsupported CRS requested. Must be "%s" and not "%s".' % (self.crs, params['crs']), 'InvalidCRS')
|
|
||||||
if params['bbox'][0] >= params['bbox'][2]:
|
if params['bbox'][0] >= params['bbox'][2]:
|
||||||
raise OGCException("BBOX values don't make sense. minx is greater than maxx.")
|
raise OGCException("BBOX values don't make sense. minx is greater than maxx.")
|
||||||
if params['bbox'][1] >= params['bbox'][3]:
|
if params['bbox'][1] >= params['bbox'][3]:
|
||||||
|
@ -204,18 +202,19 @@ class WMSBaseServiceHandler(BaseServiceHandler):
|
||||||
m = Map(params['width'], params['height'])
|
m = Map(params['width'], params['height'])
|
||||||
if params.has_key('transparent') and params['transparent'] == 'FALSE':
|
if params.has_key('transparent') and params['transparent'] == 'FALSE':
|
||||||
m.background = params['bgcolor']
|
m.background = params['bgcolor']
|
||||||
maplayers = self.mapfactory.getlayers()
|
maplayers = self.mapfactory.layers
|
||||||
mapstyles = self.mapfactory.getstyles()
|
mapstyles = self.mapfactory.styles
|
||||||
for layername in params['layers']:
|
for layername in params['layers']:
|
||||||
for layer in maplayers:
|
try:
|
||||||
if layer.name() == layername:
|
layer = maplayers[layername]
|
||||||
for stylename in layer.styles:
|
except KeyError:
|
||||||
if stylename in mapstyles.keys():
|
raise OGCException('Layer not defined: %s.' % layername, 'LayerNotDefined')
|
||||||
m.append_style(stylename, mapstyles[stylename])
|
for stylename in layer.styles:
|
||||||
m.layers.append(layer)
|
if stylename in mapstyles.keys():
|
||||||
if len(m.layers) != len(params['layers']):
|
m.append_style(stylename, mapstyles[stylename])
|
||||||
badnames = [ layername for layername in params['layers'] if layername not in [ layer.name() for layer in m.layers ] ]
|
else:
|
||||||
raise OGCException('The following layers are not defined by this server: %s.' % ','.join(badnames), 'LayerNotDefined')
|
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]))
|
m.zoom_to_box(Envelope(params['bbox'][0], params['bbox'][1], params['bbox'][2], params['bbox'][3]))
|
||||||
im = Image(params['width'], params['height'])
|
im = Image(params['width'], params['height'])
|
||||||
render(m, im)
|
render(m, im)
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
# $Id$
|
# $Id$
|
||||||
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
from lxml import etree as ElementTree
|
||||||
|
from StringIO import StringIO
|
||||||
|
from traceback import print_tb
|
||||||
|
from sys import exc_info
|
||||||
|
|
||||||
class OGCException(Exception):
|
class OGCException(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -29,11 +33,20 @@ class ServerConfigurationError(Exception):
|
||||||
|
|
||||||
class BaseExceptionHandler:
|
class BaseExceptionHandler:
|
||||||
|
|
||||||
def getexcetree(self, exc):
|
def __init__(self, debug):
|
||||||
|
self.debug = debug
|
||||||
|
|
||||||
|
def getcontent(self):
|
||||||
|
excinfo = exc_info()
|
||||||
ogcexcetree = deepcopy(self.xmltemplate)
|
ogcexcetree = deepcopy(self.xmltemplate)
|
||||||
e = ogcexcetree.find(self.xpath)
|
e = ogcexcetree.find(self.xpath)
|
||||||
if len(exc.args) > 0:
|
if self.debug:
|
||||||
e.text = exc.args[0]
|
fh = StringIO()
|
||||||
if len(exc.args) > 1:
|
print_tb(excinfo[2], None, fh)
|
||||||
e.set('code', exc.args[1])
|
fh.seek(0)
|
||||||
return ogcexcetree
|
e.text = '\n' + fh.read() + '\n' + str(excinfo[0]) + ': ' + ', '.join(excinfo[1].args) + '\n'
|
||||||
|
elif len(excinfo[1].args) > 0:
|
||||||
|
e.text = excinfo[1].args[0]
|
||||||
|
if isinstance(excinfo[1], OGCException) and len(excinfo[1].args) > 1:
|
||||||
|
e.set('code', excinfo[1].args[1])
|
||||||
|
return ElementTree.tostring(ogcexcetree)
|
|
@ -140,7 +140,7 @@ class ServiceHandler(WMSBaseServiceHandler):
|
||||||
rootlayersrs = rootlayerelem.find('SRS')
|
rootlayersrs = rootlayerelem.find('SRS')
|
||||||
rootlayersrs.text = str(self.crs)
|
rootlayersrs.text = str(self.crs)
|
||||||
|
|
||||||
for layer in self.mapfactory.getlayers():
|
for layer in self.mapfactory.layers.values():
|
||||||
layername = ElementTree.Element('Name')
|
layername = ElementTree.Element('Name')
|
||||||
layername.text = layer.name()
|
layername.text = layer.name()
|
||||||
layertitle = ElementTree.Element('Title')
|
layertitle = ElementTree.Element('Title')
|
||||||
|
@ -172,6 +172,11 @@ class ServiceHandler(WMSBaseServiceHandler):
|
||||||
response = Response('application/vnd.ogc.wms_xml', self.capabilities)
|
response = Response('application/vnd.ogc.wms_xml', self.capabilities)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def GetMap(self, params):
|
||||||
|
if str(params['srs']) != str(self.crs):
|
||||||
|
raise OGCException('Unsupported SRS requested. Must be "%s" and not "%s".' % (self.crs, params['crs']), 'InvalidCRS')
|
||||||
|
return WMSBaseServiceHandler.GetMap(self, params)
|
||||||
|
|
||||||
class ExceptionHandler(BaseExceptionHandler):
|
class ExceptionHandler(BaseExceptionHandler):
|
||||||
|
|
||||||
mimetype = "application/vnd.ogc.se_xml"
|
mimetype = "application/vnd.ogc.se_xml"
|
||||||
|
|
|
@ -126,7 +126,7 @@ class ServiceHandler(WMSBaseServiceHandler):
|
||||||
servicee = capetree.find('{http://www.opengis.net/wms}Service')
|
servicee = capetree.find('{http://www.opengis.net/wms}Service')
|
||||||
for item in self.CONF_SERVICE:
|
for item in self.CONF_SERVICE:
|
||||||
if self.conf.has_option('service', item[0]):
|
if self.conf.has_option('service', item[0]):
|
||||||
value = self.conf.get('service', item[0])
|
value = self.conf.get('service', item[0]).strip()
|
||||||
try:
|
try:
|
||||||
item[2](value)
|
item[2](value)
|
||||||
except:
|
except:
|
||||||
|
@ -147,7 +147,7 @@ class ServiceHandler(WMSBaseServiceHandler):
|
||||||
rootlayercrs = rootlayerelem.find('{http://www.opengis.net/wms}CRS')
|
rootlayercrs = rootlayerelem.find('{http://www.opengis.net/wms}CRS')
|
||||||
rootlayercrs.text = str(self.crs)
|
rootlayercrs.text = str(self.crs)
|
||||||
|
|
||||||
for layer in self.mapfactory.getlayers():
|
for layer in self.mapfactory.layers.values():
|
||||||
layername = ElementTree.Element('Name')
|
layername = ElementTree.Element('Name')
|
||||||
layername.text = layer.name()
|
layername.text = layer.name()
|
||||||
layertitle = ElementTree.Element('Title')
|
layertitle = ElementTree.Element('Title')
|
||||||
|
@ -190,6 +190,8 @@ class ServiceHandler(WMSBaseServiceHandler):
|
||||||
def GetMap(self, params):
|
def GetMap(self, params):
|
||||||
if params['width'] > int(self.conf.get('service', 'maxwidth')) or params['height'] > int(self.conf.get('service', 'maxheight')):
|
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.')
|
raise OGCException('Requested map size exceeds limits set by this server.')
|
||||||
|
if str(params['crs']) != str(self.crs):
|
||||||
|
raise OGCException('Unsupported CRS requested. Must be "%s" and not "%s".' % (self.crs, params['crs']), 'InvalidCRS')
|
||||||
return WMSBaseServiceHandler.GetMap(self, params)
|
return WMSBaseServiceHandler.GetMap(self, params)
|
||||||
|
|
||||||
class ExceptionHandler(BaseExceptionHandler):
|
class ExceptionHandler(BaseExceptionHandler):
|
||||||
|
|
|
@ -89,15 +89,27 @@ API, look in demo/python, or in docs/epydocs.
|
||||||
|
|
||||||
The server needs a python module, with code that looks like this:
|
The server needs a python module, with code that looks like this:
|
||||||
|
|
||||||
from mapnik.ogcserver.wms import BaseWMSFactory
|
from mapnik.ogcserver.WMS import BaseWMSFactory
|
||||||
|
|
||||||
class MapFactory:
|
class WMSFactory(BaseWMSFactory):
|
||||||
|
|
||||||
def __init(self):
|
def __init(self):
|
||||||
|
BaseWMSFactory.__init__(self)
|
||||||
|
sty = Style()
|
||||||
...
|
...
|
||||||
|
self.register_style('stylename', sty)
|
||||||
def getlayers(self):
|
|
||||||
|
lyr = Layer(name='layername')
|
||||||
...
|
...
|
||||||
|
self.register_layer(lyr)
|
||||||
def getstyles(self):
|
|
||||||
...
|
The rules for writing this class are:
|
||||||
|
|
||||||
|
- It MUST be called 'WMSFactory'.
|
||||||
|
- It MUST sub-class mapnik.ogcserver.WMS.BaseWMSFactory.
|
||||||
|
- The __init__ MUST call the base class'.
|
||||||
|
- Layers MUST be named with the 'name' parameter to the constructor.
|
||||||
|
- style and layer names are meant for machine readability, not human. Keep
|
||||||
|
them short and simple, without spaces or special characters.
|
||||||
|
- The layers must have at least one style associated with them (a default).
|
||||||
|
- No Map() object is used or needed here.
|
|
@ -36,8 +36,4 @@ epsg=4326
|
||||||
# supporting the service for example. This is NOT the online
|
# supporting the service for example. This is NOT the online
|
||||||
# resource pointing to the CGI.
|
# resource pointing to the CGI.
|
||||||
|
|
||||||
onlineresource=http://www.mapnik.org/
|
onlineresource=http://www.mapnik.org/
|
||||||
|
|
||||||
[contact]
|
|
||||||
name=
|
|
||||||
email=
|
|
Loading…
Reference in a new issue