| 1 | # $Id$ |
|---|
| 2 | |
|---|
| 3 | Mapnik OGC Server |
|---|
| 4 | ----------------- |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | Introduction |
|---|
| 8 | ------------ |
|---|
| 9 | |
|---|
| 10 | Mapnik provides a server package to allow the publishing of maps |
|---|
| 11 | through the open and standard WMS interface published by the Open Geospatial |
|---|
| 12 | Consortium (OGC). It is in implemented in Python, around the core Mapnik C++ |
|---|
| 13 | library. |
|---|
| 14 | |
|---|
| 15 | This is the very first implementation of a WMS for Mapnik. Although inital |
|---|
| 16 | testing seems to suggest it works well, there may be bugs, and it lacks some |
|---|
| 17 | useful features. Comments, contributions, and requests for help should all be |
|---|
| 18 | directed to the Mapnik mailing list. |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | Features |
|---|
| 22 | -------- |
|---|
| 23 | |
|---|
| 24 | - WMS 1.1.1 and 1.3.0 |
|---|
| 25 | - CGI/FastCGI, WSGI, mod_python |
|---|
| 26 | - Supports all 3 requests: GetCapabilities, GetMap and GetFeatureInfo |
|---|
| 27 | - JPEG/PNG output |
|---|
| 28 | - XML/INIMAGE/BLANK error handling |
|---|
| 29 | - Multiple named styles support |
|---|
| 30 | - Reprojection support |
|---|
| 31 | - Supported layer metadata: title, abstract |
|---|
| 32 | - Ability to request all layers with LAYERS=__all__ |
|---|
| 33 | |
|---|
| 34 | |
|---|
| 35 | Caveats |
|---|
| 36 | ---------------- |
|---|
| 37 | - GetFeatureInfo supports text/plain output only |
|---|
| 38 | - PNG256(8-bit PNG not yet supported) |
|---|
| 39 | - CGI/FastCGI interface needs to be able to write to tempfile.gettempdir() (most likely "/tmp") |
|---|
| 40 | - Need to be further evaluated for thread safety |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | Dependencies |
|---|
| 44 | ------------ |
|---|
| 45 | |
|---|
| 46 | Please properly install the following before proceeding further: |
|---|
| 47 | |
|---|
| 48 | - Mapnik python bindings (which will also install the `ogcserver` module code) |
|---|
| 49 | - lxml (http://codespeak.net/lxml/) |
|---|
| 50 | - PIL (http://www.pythonware.com/products/pil) |
|---|
| 51 | |
|---|
| 52 | For the CGI/FastCGI interface also install: |
|---|
| 53 | |
|---|
| 54 | - jonpy (http://jonpy.sourceforge.net/) |
|---|
| 55 | |
|---|
| 56 | |
|---|
| 57 | Installation |
|---|
| 58 | ------------ |
|---|
| 59 | |
|---|
| 60 | - The OGC Server uses the Mapnik interface to the Proj.4 library for projection support |
|---|
| 61 | and depends on integer EPSG codes. Confirm that you have installed Proj.4 with |
|---|
| 62 | all necessary data files (http://trac.osgeo.org/proj/wiki/FAQ) and have added any custom |
|---|
| 63 | projections to the 'epsg' file usually located at '/usr/local/share/proj/epsg'. |
|---|
| 64 | |
|---|
| 65 | - Test that the server code is available and installed properly by importing it within a |
|---|
| 66 | python interpreter:: |
|---|
| 67 | |
|---|
| 68 | >>> from mapnik import ogcserver |
|---|
| 69 | >>> # no error means proper installation |
|---|
| 70 | |
|---|
| 71 | - There is a sample python script called "wms.py" in the utils/ogcserver folder of the |
|---|
| 72 | Mapnik source code that will work for both CGI and FastCGI operations. Where to place it |
|---|
| 73 | will depend on your server choice and configuration and is beyond this documentation. |
|---|
| 74 | For information on FastCGI go to http://www.fastcgi.com/. |
|---|
| 75 | |
|---|
| 76 | |
|---|
| 77 | Configuring the server |
|---|
| 78 | ---------------------- |
|---|
| 79 | |
|---|
| 80 | - You will need to create two simple python scripts: |
|---|
| 81 | |
|---|
| 82 | 1) The web-accessible python script ('wms.py') which will import the |
|---|
| 83 | ogcserver module code and associate itself with the 'ogcserver.conf' |
|---|
| 84 | configuration file. The code of this script will depend upon whether |
|---|
| 85 | you deploy the server as cgi/fastcgi/wsgi/mod_python. See the Mapnik |
|---|
| 86 | Community Wiki for examples: http://trac.mapnik.org/wiki/OgcServer and |
|---|
| 87 | see the cgi sample in the /utils/ogcserver folder. |
|---|
| 88 | |
|---|
| 89 | 2) A 'map_factory' script which loads your layers and styles. Samples of this |
|---|
| 90 | script can be found below. |
|---|
| 91 | |
|---|
| 92 | |
|---|
| 93 | - Next you need to edit the ogcserver.conf file to: |
|---|
| 94 | |
|---|
| 95 | 1) Point to the 'map_factory' script by using the "module" parameter |
|---|
| 96 | |
|---|
| 97 | 2) Fill out further settings for the server. |
|---|
| 98 | |
|---|
| 99 | Edit the configuration file to your liking, the comments within the file will |
|---|
| 100 | help you further. Be sure to, at the very minimum, edit the "module" |
|---|
| 101 | parameter. The server will not work without setting it properly first. |
|---|
| 102 | |
|---|
| 103 | |
|---|
| 104 | Defining Layers and Styles |
|---|
| 105 | -------------------------- |
|---|
| 106 | |
|---|
| 107 | The ogcserver obviously needs layers to publish and styles for how to display those layers. |
|---|
| 108 | |
|---|
| 109 | You create your layers and styles in the 'map_factory' script. |
|---|
| 110 | |
|---|
| 111 | For now this can be done by either loading an XML mapfile inside that script using the |
|---|
| 112 | 'loadXML()' function or by writing your layers and styles in python code, or both. |
|---|
| 113 | |
|---|
| 114 | If you load your layers and styles using an existing XML mapfile the 'map_factory' module |
|---|
| 115 | should look like:: |
|---|
| 116 | |
|---|
| 117 | from mapnik.ogcserver.WMS import BaseWMSFactory |
|---|
| 118 | |
|---|
| 119 | class WMSFactory(BaseWMSFactory): |
|---|
| 120 | def __init__(self): |
|---|
| 121 | BaseWMSFactory.__init__(self) |
|---|
| 122 | self.loadXML('/full/path/to/mapfile.xml') |
|---|
| 123 | self.finalize() |
|---|
| 124 | |
|---|
| 125 | Or if you want to define your layers and styles in pure python you might |
|---|
| 126 | have a 'map_factory' more like:: |
|---|
| 127 | |
|---|
| 128 | from mapnik.ogcserver.WMS import BaseWMSFactory |
|---|
| 129 | from mapnik import * |
|---|
| 130 | |
|---|
| 131 | SHAPEFILE = '/path/to/world_borders.shp' |
|---|
| 132 | PROJ4_STRING = '+init=epsg:4326' |
|---|
| 133 | |
|---|
| 134 | class WMSFactory(BaseWMSFactory): |
|---|
| 135 | def __init__(self): |
|---|
| 136 | BaseWMSFactory.__init__(self) |
|---|
| 137 | sty,rl = Style(),Rule() |
|---|
| 138 | poly = PolygonSymbolizer(Color('#f2eff9')) |
|---|
| 139 | line = LineSymbolizer(Color('steelblue'),.1) |
|---|
| 140 | rl.symbols.extend([poly,line]) |
|---|
| 141 | sty.rules.append(rl) |
|---|
| 142 | self.register_style('world_style',sty) |
|---|
| 143 | lyr = Layer('world',PROJ4_STRING) |
|---|
| 144 | lyr.datasource = Shapefile(file=SHAPEFILE) |
|---|
| 145 | lyr.title = 'World Borders' |
|---|
| 146 | lyr.abstract = 'Country Borders of the World' |
|---|
| 147 | self.register_layer(lyr,'world_style',('world_style',)) |
|---|
| 148 | self.finalize() |
|---|
| 149 | |
|---|
| 150 | The rules for writing this class are: |
|---|
| 151 | |
|---|
| 152 | - It MUST be called 'WMSFactory'. |
|---|
| 153 | - It MUST sub-class mapnik.ogcserver.WMS.BaseWMSFactory. |
|---|
| 154 | - The __init__ MUST call the base class. |
|---|
| 155 | - Layers MUST be named with the first parameter to the constructor. |
|---|
| 156 | - Layers MUST define an EPSG projection in the second parameter of the |
|---|
| 157 | constructor. This implies that the underlying data must be in an EPSG |
|---|
| 158 | projection already. |
|---|
| 159 | - Style and layer names are meant for machine readability, not human. Keep |
|---|
| 160 | them short and simple, without spaces or special characters. |
|---|
| 161 | - For human readable info, set the title and abstract properties on the layer |
|---|
| 162 | object. |
|---|
| 163 | - DO NOT register styles using layer.styles.append(), instead, provide style |
|---|
| 164 | information to the register_layer() call:: |
|---|
| 165 | |
|---|
| 166 | register_layer(layerobject, defaultstylename, (tuple of alternative style names,)) |
|---|
| 167 | |
|---|
| 168 | - No Map() object is used or needed here. |
|---|
| 169 | - Be sure to call self.finalize() once you have registered everything! This will |
|---|
| 170 | validate everything and let you know if there are any problems. |
|---|
| 171 | - For a layer to be queryable via GetFeatureInfo, simply set the 'queryable' |
|---|
| 172 | property to True:: |
|---|
| 173 | |
|---|
| 174 | lyr.queryable = True |
|---|
| 175 | |
|---|
| 176 | |
|---|
| 177 | To Do |
|---|
| 178 | ----- |
|---|
| 179 | |
|---|
| 180 | - Investigate moving to cElementTree from lxml. |
|---|
| 181 | - Add some internal "caching" for performance improvements. |
|---|
| 182 | - Switch to using C/C++ libs for image generation, instead of PIL (also |
|---|
| 183 | requires core changes). PIL requirement will remain for INIMAGE/BLANK |
|---|
| 184 | error handling. |
|---|