#!/usr/bin/env python # -*- coding: utf-8 -*- # # $Id$ # # This file is part of Mapnik (c++ mapping toolkit) # Copyright (C) 2005 Jean-Francois Doyon # # Mapnik is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Import everything. In this case this is safe, in more complex systems, you # will want to be more selective. import sys try: import mapnik2 except: print '\n\nThe mapnik library and python bindings must have been compiled and \ installed successfully before running this script.\n\n' sys.exit(1) try: import cairo HAS_PYCAIRO_MODULE = True except ImportError: HAS_PYCAIRO_MODULE = False # Instanciate a map, giving it a width and height. Remember: the word "map" is # reserved in Python! :) m = mapnik2.Map(800,600,"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs") # Set its background colour. More on colours later ... m.background = mapnik2.Color('white') # Now we can start adding layers, in stacking order (i.e. bottom layer first) # Canadian Provinces (Polygons) # Instanciate a layer. The parameters depend on the type of data: # shape: # type='shape' # file='/path/to/shape' # raster: # type='raster' # file='/path/to/raster' # postgis: # type='postgis' # host='127.0.0.1' # dbname='mydatabase' # user='myusername' # password='mypassword' # table= TODO provpoly_lyr = mapnik2.Layer('Provinces') provpoly_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" provpoly_lyr.datasource = mapnik2.Shapefile(file='../data/boundaries', encoding='latin1') # We then define a style for the layer. A layer can have one or many styles. # Styles are named, so they can be shared across different layers. # Multiple styles per layer behaves functionally like multiple layers. The # data is completely re-scanned for each style within one layer, and a style # will be drawn entirely "above" the previous one. Performance wise using # multiple styles in one layer is the same has having multiple layers. # The paradigm is useful mostly as a convenience. provpoly_style = mapnik2.Style() # A Style needs one or more rules. A rule will normally consist of a filter # for feature selection, and one or more symbolizers. provpoly_rule_on = mapnik2.Rule() # A Expression() allows the selection of features to which the symbology will # be applied. More on Mapnik expressions can be found in Tutorial #2. # A given feature can only match one filter per rule per style. provpoly_rule_on.filter = mapnik2.Expression("[NAME_EN] = 'Ontario'") # Here a symbolizer is defined. Available are: # - LineSymbolizer(Color(),<width>) # - LineSymbolizer(Stroke()) # - PolygonSymbolizer(Color()) # - PointSymbolizer(<file>,<type>,<width>,<height>) # Some of them can accept a Color() instance, which can be created with: # - Color(<red>, <green>, <blue>) # - Color(<red>, <green>, <blue>, <alpha>) # - Color(<string>) where <string> will be something like '#00FF00' # or '#0f0' or 'green' provpoly_rule_on.symbols.append(mapnik2.PolygonSymbolizer(mapnik2.Color(250, 190, 183))) provpoly_style.rules.append(provpoly_rule_on) provpoly_rule_qc = mapnik2.Rule() provpoly_rule_qc.filter = mapnik2.Expression("[NOM_FR] = 'Québec'") provpoly_rule_qc.symbols.append(mapnik2.PolygonSymbolizer(mapnik2.Color(217, 235, 203))) provpoly_style.rules.append(provpoly_rule_qc) # Add the style to the map, giving it a name. This is the name that will be # used to refer to it from here on. Having named styles allows them to be # re-used throughout the map. m.append_style('provinces', provpoly_style) # Then associate the style to the layer itself. provpoly_lyr.styles.append('provinces') # Then add the layer to the map. In reality, it's the order in which you # append them to the map that will determine the drawing order, though by # convention it is recommended to define them in drawing order as well. m.layers.append(provpoly_lyr) # Drainage # A simple example ... qcdrain_lyr = mapnik2.Layer('Quebec Hydrography') qcdrain_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" qcdrain_lyr.datasource = mapnik2.Shapefile(file='../data/qcdrainage') qcdrain_style = mapnik2.Style() qcdrain_rule = mapnik2.Rule() qcdrain_rule.filter = mapnik2.Expression('[HYC] = 8') qcdrain_rule.symbols.append(mapnik2.PolygonSymbolizer(mapnik2.Color(153, 204, 255))) qcdrain_style.rules.append(qcdrain_rule) m.append_style('drainage', qcdrain_style) qcdrain_lyr.styles.append('drainage') m.layers.append(qcdrain_lyr) # In this case, we have 2 data sets with similar schemas (same filtering # attributes, and same desired style), so we're going to # re-use the style defined in the above layer for the next one. ondrain_lyr = mapnik2.Layer('Ontario Hydrography') ondrain_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" ondrain_lyr.datasource = mapnik2.Shapefile(file='../data/ontdrainage') ondrain_lyr.styles.append('drainage') m.layers.append(ondrain_lyr) # Provincial boundaries provlines_lyr = mapnik2.Layer('Provincial borders') provlines_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" provlines_lyr.datasource = mapnik2.Shapefile(file='../data/boundaries_l') # Here we define a "dash dot dot dash" pattern for the provincial boundaries. provlines_stk = mapnik2.Stroke() provlines_stk.add_dash(8, 4) provlines_stk.add_dash(2, 2) provlines_stk.add_dash(2, 2) provlines_stk.color = mapnik2.Color('black') provlines_stk.width = 1.0 provlines_style = mapnik2.Style() provlines_rule = mapnik2.Rule() provlines_rule.symbols.append(mapnik2.LineSymbolizer(provlines_stk)) provlines_style.rules.append(provlines_rule) m.append_style('provlines', provlines_style) provlines_lyr.styles.append('provlines') m.layers.append(provlines_lyr) # Roads 3 and 4 (The "grey" roads) roads34_lyr = mapnik2.Layer('Roads') roads34_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" # create roads datasource (we're going to re-use it later) roads34_lyr.datasource = mapnik2.Shapefile(file='../data/roads') roads34_style = mapnik2.Style() roads34_rule = mapnik2.Rule() roads34_rule.filter = mapnik2.Expression('([CLASS] = 3) or ([CLASS] = 4)') # With lines of a certain width, you can control how the ends # are closed off using line_cap as below. roads34_rule_stk = mapnik2.Stroke() roads34_rule_stk.color = mapnik2.Color(171,158,137) roads34_rule_stk.line_cap = mapnik2.line_cap.ROUND_CAP # Available options are: # line_cap: BUTT_CAP, SQUARE_CAP, ROUND_CAP # line_join: MITER_JOIN, MITER_REVERT_JOIN, ROUND_JOIN, BEVEL_JOIN # And one last Stroke() attribute not used here is "opacity", which # can be set to a numerical value. roads34_rule_stk.width = 2.0 roads34_rule.symbols.append(mapnik2.LineSymbolizer(roads34_rule_stk)) roads34_style.rules.append(roads34_rule) m.append_style('smallroads', roads34_style) roads34_lyr.styles.append('smallroads') m.layers.append(roads34_lyr) # Roads 2 (The thin yellow ones) roads2_lyr = mapnik2.Layer('Roads') roads2_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" # Just get a copy from roads34_lyr roads2_lyr.datasource = roads34_lyr.datasource roads2_style_1 = mapnik2.Style() roads2_rule_1 = mapnik2.Rule() roads2_rule_1.filter = mapnik2.Expression('[CLASS] = 2') roads2_rule_stk_1 = mapnik2.Stroke() roads2_rule_stk_1.color = mapnik2.Color(171,158,137) roads2_rule_stk_1.line_cap = mapnik2.line_cap.ROUND_CAP roads2_rule_stk_1.width = 4.0 roads2_rule_1.symbols.append(mapnik2.LineSymbolizer(roads2_rule_stk_1)) roads2_style_1.rules.append(roads2_rule_1) m.append_style('road-border', roads2_style_1) roads2_style_2 = mapnik2.Style() roads2_rule_2 = mapnik2.Rule() roads2_rule_2.filter = mapnik2.Expression('[CLASS] = 2') roads2_rule_stk_2 = mapnik2.Stroke() roads2_rule_stk_2.color = mapnik2.Color(255,250,115) roads2_rule_stk_2.line_cap = mapnik2.line_cap.ROUND_CAP roads2_rule_stk_2.width = 2.0 roads2_rule_2.symbols.append(mapnik2.LineSymbolizer(roads2_rule_stk_2)) roads2_style_2.rules.append(roads2_rule_2) m.append_style('road-fill', roads2_style_2) roads2_lyr.styles.append('road-border') roads2_lyr.styles.append('road-fill') m.layers.append(roads2_lyr) # Roads 1 (The big orange ones, the highways) roads1_lyr = mapnik2.Layer('Roads') roads1_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" roads1_lyr.datasource = roads34_lyr.datasource roads1_style_1 = mapnik2.Style() roads1_rule_1 = mapnik2.Rule() roads1_rule_1.filter = mapnik2.Expression('[CLASS] = 1') roads1_rule_stk_1 = mapnik2.Stroke() roads1_rule_stk_1.color = mapnik2.Color(188,149,28) roads1_rule_stk_1.line_cap = mapnik2.line_cap.ROUND_CAP roads1_rule_stk_1.width = 7.0 roads1_rule_1.symbols.append(mapnik2.LineSymbolizer(roads1_rule_stk_1)) roads1_style_1.rules.append(roads1_rule_1) m.append_style('highway-border', roads1_style_1) roads1_style_2 = mapnik2.Style() roads1_rule_2 = mapnik2.Rule() roads1_rule_2.filter = mapnik2.Expression('[CLASS] = 1') roads1_rule_stk_2 = mapnik2.Stroke() roads1_rule_stk_2.color = mapnik2.Color(242,191,36) roads1_rule_stk_2.line_cap = mapnik2.line_cap.ROUND_CAP roads1_rule_stk_2.width = 5.0 roads1_rule_2.symbols.append(mapnik2.LineSymbolizer(roads1_rule_stk_2)) roads1_style_2.rules.append(roads1_rule_2) m.append_style('highway-fill', roads1_style_2) roads1_lyr.styles.append('highway-border') roads1_lyr.styles.append('highway-fill') m.layers.append(roads1_lyr) # Populated Places popplaces_lyr = mapnik2.Layer('Populated Places') popplaces_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs" popplaces_lyr.datasource = mapnik2.Shapefile(file='../data/popplaces',encoding='latin1') popplaces_style = mapnik2.Style() popplaces_rule = mapnik2.Rule() # And here we have a TextSymbolizer, used for labeling. # The first parameter is the name of the attribute to use as the source of the # text to label with. Then there is font size in points (I think?), and colour. popplaces_text_symbolizer = mapnik2.TextSymbolizer(mapnik2.Expression("[GEONAME]"), 'DejaVu Sans Book', 10, mapnik2.Color('black')) # We set a "halo" around the text, which looks like an outline if thin enough, # or an outright background if large enough. popplaces_text_symbolizer.label_placement= mapnik2.label_placement.POINT_PLACEMENT popplaces_text_symbolizer.halo_fill = mapnik2.Color('white') popplaces_text_symbolizer.halo_radius = 1 popplaces_text_symbolizer.avoid_edges = True #popplaces_text_symbolizer.minimum_padding = 30 popplaces_rule.symbols.append(popplaces_text_symbolizer) popplaces_style.rules.append(popplaces_rule) m.append_style('popplaces', popplaces_style) popplaces_lyr.styles.append('popplaces') m.layers.append(popplaces_lyr) # Draw map # Set the initial extent of the map in 'master' spherical Mercator projection m.zoom_to_box(mapnik2.Box2d(-8024477.28459,5445190.38849,-7381388.20071,5662941.44855)) # Render two maps, two PNGs, one JPEG. im = mapnik2.Image(m.width,m.height) mapnik2.render(m, im) # Save image to files images_ = [] im.save('demo.png', 'png') # true-colour RGBA images_.append('demo.png') # old behavior, now can do 'png8:c=256' im.save('demo256.png', 'png256') # save to palette based (max 256 colours) png images_.append('demo256.png') im.save('demo64_binary_transparency.png', 'png8:c=64:t=1') images_.append('demo64_binary_transparency.png') im.save('demo128_colors_hextree_no_alpha.png', 'png8:c=100:m=h:t=0') images_.append('demo128_colors_hextree_no_alpha.png') im.save('demo_high.jpg', 'jpeg100') images_.append('demo_high.jpg') im.save('demo_low.jpg', 'jpeg50') images_.append('demo_low.jpg') # Render cairo examples if HAS_PYCAIRO_MODULE and mapnik2.has_pycairo(): svg_surface = cairo.SVGSurface('demo.svg', m.width,m.height) mapnik2.render(m, svg_surface) svg_surface.finish() images_.append('demo.svg') pdf_surface = cairo.PDFSurface('demo.pdf', m.width,m.height) mapnik2.render(m, pdf_surface) images_.append('demo.pdf') pdf_surface.finish() postscript_surface = cairo.PSSurface('demo.ps', m.width,m.height) mapnik2.render(m, postscript_surface) images_.append('demo.ps') postscript_surface.finish() else: print '\n\nPycairo not available...', if mapnik2.has_cairo(): print ' will render Cairo formats using alternative method' mapnik2.render_to_file(m,'demo.pdf') images_.append('demo.pdf') mapnik2.render_to_file(m,'demo.ps') images_.append('demo.ps') mapnik2.render_to_file(m,'demo.svg') images_.append('demo.svg') mapnik2.render_to_file(m,'demo_cairo_rgb.png','RGB24') images_.append('demo_cairo_rgb.png') mapnik2.render_to_file(m,'demo_cairo_argb.png','ARGB32') images_.append('demo_cairo_argb.png') print "\n\n", len(images_), "maps have been rendered in the current directory:" for im_ in images_: print "-", im_ print "\n\nHave a look!\n\n" mapnik2.save_map(m,"map.xml")