User:Moresby/Understanding Mapnik/Representing map designs in XML
So far each map which we have generated has required its own Python program, requiring objects to be created, modified and joined together to produce the required output. An alternative approach is to use an XML file to represent our map design, and to allow Mapnik to generate the appropriate objects itself. For example, the map we generated earlier as an example of using layers to draw points and lines can be written in XML as follows:
<?xml version='1.0'?>
<Map background-color='ghostwhite'>
<Style name='line_style'>
<Rule>
<LineSymbolizer/>
</Rule>
</Style>
<Style name='point_style'>
<Rule>
<PointSymbolizer file='circle_red_16x16.png'/>
</Rule>
</Style>
<Layer name='line_layer'>
<StyleName>line_style</StyleName>
<Datasource>
<Parameter name='file'>data-roads.csv</Parameter>
<Parameter name='type'>csv</Parameter>
</Datasource>
</Layer>
<Layer name='point_layer'>
<StyleName>point_style</StyleName>
<Datasource>
<Parameter name='file'>data-places.csv</Parameter>
<Parameter name='type'>csv</Parameter>
</Datasource>
</Layer>
</Map>
- Extensible Markup Language (XML) is a language used to represent structured data. Sections begin with a start tag such as
<Style>
and<Layer>
and end with a matching end tag such as</Style>
and</Layer>
. Where there is nothing between a start and end tag, an empty tag can be used instead, such as<LineSymbolizer/>
. As well as content (including other tags) between the start and end tags, start tags can have attribute key-value pairs attached to them, such asname='point_style'
. - Line one is simply a marker to show that this file is an XML file.
- Lines two to 27 contain the single <Map> element, which represents the design of the map in question.
- Within the <Map> element, we have the definition of two styles and two layers.
- The first style is defined using the
<Style>
element at lines three to seven. We have to give the style a name, which is done using thename='line_style'
attribute. - This style contains just one rule, which contains one LineSymbolizer. Because we are happy with the default settings for each of these objects, we need no more than simply specify XML elements with the appropriate names.
- The second style is defined at lines eight to twelve. This also has a name, and contains a PointSymbolizer rather than a LineSymbolizer. Because we are specifying an image file to use, we include the
file='circle_red_16x16.png'
attribute. - The two layers are defined at lines 13 to 19 and 20 to 26. Each has its own name, the name of the Style object to be used and details of the data source.
We still need a program to tell Mapnik to load and process this file, but this can now be very simple:
#!/usr/bin/python
import sys
import mapnik
import re
if len(sys.argv) != 2:
print "Usage: python generate-map.py <filename>"
sys.exit(1)
infile = sys.argv[1]
r = re.search(r'([^/]*)\.xml$', infile)
if r:
outfile = r.group(1) + ".png"
else:
outfile = infile + ".png"
m = mapnik.Map(480, 320)
mapnik.load_map(m, infile)
m.zoom_to_box(mapnik.Box2d(0, 0, 480, 320))
mapnik.render_to_file(m, outfile, 'png')
Save the XML data in a file called 140-layers.xml
and the Python program in a file called generate-map.py
and run the following command:
python generate-map.py 140-layers.xml
You should see no error messages, and you should see a new file in your working directory called 140-layers.png. This is a new map image, and should be a light-coloured rectangle 480 pixels wide by 320 pixels high, with a series of red circles interconnected by black lines, as shown above.
A more general-purpose Python program to generate maps from XML files is Nik2img.