cleanup pass

springmeyer 2012-01-09 10:53:40 -08:00
parent a14d7000f2
commit 01122d674c

@ -1,96 +1,155 @@
## Getting started in Python
# Tutorial 1 -- Getting started in Python
### Step 1
## Overview
Make sure you have mapnik installed. Depending on your installation path you may need to modify your `PYTHONPATH`, `/etc/ld.so.conf` or export `LD_LIBRARY_PATH`, or whatever your system requires.
This tutorial will ensure that Mapnik and its python bindings are properly installed and introduce you to some of the basic programming concepts for Mapnik.
The simple check is to start python interpreter from a command line by typing python
and then just type:
## Step 1: check installation
Make sure you have mapnik installed. You should be able to open a terminal and type:
mapnik-config -v # should return a version number.
This tutorial expects Mapnik 2.x or greater. Older versions do not provide the `mapnik-config` program, so we recommend upgrading.
Next test the python bindings. You should be able to open a terminal and type:
python -c "import mapnik;print mapnik.__file__" # should return the path to the python bindings and no errors
If the above does not work (e.g. throws an `ImportError`) then please go back and ensure Mapnik is properly [installed](Mapnik-Installation). If you need help, sign up for the [mailing list](http://mapnik.org/contact/) to ask questions or join the [#mapnik room on freenode IRC](irc://irc.freenode.net/mapnik)
## Step 2
Now, we need some data to render. Let's use a shapefile of world border polygons from http://naturalearthdata.com. Download the data from this wiki's local cache [here](data/110m-admin-0-countries.zip) or directly from the [Natural Earth Data site](http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/110m-admin-0-countries.zip). Unzip the archive in an easily accessible location of your choosing. In *Step 3* we will be referencing the path to this shapefile in python code, so make sure you know where you put it.
Once unzipped, you should see four files like:
ne_110m_admin_0_countries.shp
ne_110m_admin_0_countries.shx
ne_110m_admin_0_countries.dbf
ne_110m_admin_0_countries.prj
To download and unzip on the command line with the do:
wget https://github.com/mapnik/mapnik/wiki/data/110m-admin-0-countries.zip
unzip 110m-admin-0-countries.zip # creates ne_110m_admin_0_countries.shp
## Step 3
Now we're going to program in Python and Mapnik, using sample code and the python interpreter.
The idea here is not that you have to interact with Mapnik via Python, but that this is a good way to build foundational skills for how Mapnik works.
So, let's begin! Open a python interpreter simply by typing in your terminal:
python
The code below can be pasted into your interpreter. Ideally paste line by line so you can confirm each step is working. The commented lines (#) should be able to be pasted without trouble, but depending on your interpreter setting may cause errors.
### Import Mapnik
Import the Mapnik python bindings:
import mapnik
### Create a Map
```python
>>> import mapnik
m = mapnik.Map(600,300) # create a map with a given width and height in pixels
# note: m.srs will default to '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
# the 'map.srs' is the target projection of the map and can be whatever you wish
m.background = mapnik.Color('steelblue') # set background colour to 'steelblue'.
```
and if you don't see any complaints, you're on the right track. If you do, you have to check your installation again.
### Create a Style
* Note: If you have built mapnik in debug mode you should see the available datasources listed, including:
Create the Styles which determines how the data is rendered:
```python
>>> import mapnik
registered datasource : gdal
registered datasource : postgis
registered datasource : raster
registered datasource : shape
s = mapnik.Style() # style object to hold rules
r = mapnik.Rule() # rule object to hold symbolizers
# to fill a polygon we create a PolygonSymbolizer
polygon_symbolizer = mapnik.PolygonSymbolizer(mapnik.Color('#f2eff9'))
r.symbols.append(polygon_symbolizer) # add the symbolizer to the rule object
# to add outlines to a polygon we create a LineSymbolizer
line_symbolizer = mapnik.LineSymbolizer(mapnik.Color('rgb(50%,50%,50%)'),0.1)
r.symbols.append(line_symbolizer) # add the symbolizer to the rule object
s.rules.append(r) # now add the rule to the style and we're done
```
### Step 2
The code below can be pasted into your python interpreter. Ideally paste line by line so you can confirm each step is working. The commented lines (#) should be able to be pasted without trouble, but depending on your interpreter setting may cause errors.
* See the code snippet in Step 3 for code without comments.
Import the mapnik python toolkit and setup the basic map parameters
```
# import mapnik python bindings
import mapnik
# Instantiate a map object with given width, height and spatial reference system
m = mapnik.Map(600,300,"+proj=latlong +datum=WGS84")
# Set background colour to 'steelblue'.
# You can use 'named' colours, #rrggbb, #rgb or rgb(r%,g%,b%) format
m.background = mapnik.Color('steelblue')
```
* Note, Mapnik accepts any projection that Proj.4 handles. See http://spatialreference.org for the proj4 strings for various projections. For example http://spatialreference.org/ref/epsg/4326/mapnikpython/ will give you the exact parameters for EPSG 4326.
Create the Styles and Rules for the map symbology
And add the Style to the Map:
```python
# Now lets create a style and add it to the Map.
s = mapnik.Style()
# A Style can have one or more rules. A rule consists of a filter, min/max scale
# demoninators and 1..N Symbolizers. If you don't specify filter and scale denominators
# you get default values :
# Filter = 'ALL' filter (meaning symbolizer(s) will be applied to all features)
# MinScaleDenominator = 0
# MaxScaleDenominator = INF
# Lets keep things simple and use default value, but to create a map we
# we still must provide at least one Symbolizer. Here we want to fill countries polygons with
# greyish colour and draw outlines with a bit darker stroke.
r=mapnik.Rule()
r.symbols.append(mapnik.PolygonSymbolizer(mapnik.Color('#f2eff9')))
r.symbols.append(mapnik.LineSymbolizer(mapnik.Color('rgb(50%,50%,50%)'),0.1))
s.rules.append(r)
m.append_style('My Style',s) # Styles are given names only as they are applied to the map
```
Connect the style information to your map and your data
### Create a Datasource
```python
# Here we have to add our style to the Map, giving it a name.
m.append_style('My Style',s)
In *Step 2* above you should have downloaded a sample shapefile of polygons of world countries. We are now going to load that into a `mapnik.Datasource` object in python.
# Here we instantiate our data layer, first by giving it a name and srs (proj4 projections string), and then by giving it a datasource.
lyr = mapnik.Layer('world',"+proj=latlong +datum=WGS84")
# Then provide the full filesystem path to a shapefile in WGS84 or EPSG 4326 projection without the .shp extension
# A sample shapefile can be downloaded from http://mapnik-utils.googlecode.com/svn/data/world_borders.zip
lyr.datasource = mapnik.Shapefile(file='/Users/path/to/your/data/world_borders')
lyr.styles.append('My Style')
If your python interpreter was launched from the same directory as you downloaded the natural earth shapefile to you should be able to use a relative path to create the datasource like:
``` python
ds = mapnik.Shapefile(file='ne_110m_admin_0_countries.shp')
```
Finally add the layers to the map and zoom to the full extent of the data layer
Otherwise use an absolute path (exchanging `/Users/dane/Downloads/` for the correct path on your machine):
```python
m.layers.append(lyr)
m.zoom_to_box(lyr.envelope())
``` python
ds = mapnik.Shapefile(file='/Users/dane/Downloads/ne_110m_admin_0_countries.shp')
```
Finish up by making the world map image
Note: optionally (to learn about your data) you can call the `envelope()` function off the datasource object to see the full coordinate bounds of the data:
``` python
>>> ds.envelope()
Box2d(-180.0,-90.0,180.0,83.64513)
```
That shows the minx, miny, maxx, and maxy of the data. Because the above coordinates are between -180 and 180 for the x or longitude values and -90 and 90 for the y or latitude values we know this data is in *geographic* coordinates and uses degrees for units - a pretty good indication this is `WGS84 (aka EPSG:4326)`. This specific shapefile also stores this projection information as a `WKT` string in the `ne_110m_admin_0_countries.prj` file. But Mapnik need to know this specific, common spatial references system by its [Proj.4](http://trac.osgeo.org/proj/wiki/FAQ) string of `+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs`. See the `layer.srs` value below for why this matters.
### Create a Layer
Mapnik Layers are basically containers around datasources, that store useful properties.
So, lets now create a Layer object and add the datasource to it.
``` python
layer = mapnik.Layer('world') # new layer called 'world' (we could name it anything)
# note: layer.srs will default to '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
```
Note: the 'layer.srs' is the source projection of the Datasource and *must* match the projection of the coordinates of that data or else your map will likely be blank. In this case, the default `srs` Mapnik assumes happens to also match the projection of the data. When this is not the case you need to set the layer.srs to the correct value (which is beyond the scope of this tutorial).
Now attach the datasource to the layer, and reference:
```python
# Write the data to a png image called world.png in the base directory of your user
layer.datasource = ds
```
Lastly, we need to make sure the style we created above (and attached to the map) is also applied to the layer, by its string reference:
```python
layer.styles.append('My Style')
```
### Prepare the Map for rendering
This step is critical. Finally add the layer to the map and zoom to the full extent of the data layer (using `zoom_all` which will calculate the cumulative extent of all layers attached to the map). If you do not zoom the Map to the extent of the layer(s), then the rendered output will be blank.
```python
m.layers.append(layer)
m.zoom_all()
```
### Render your map
Finish up by rendering your map image:
```python
# Write the data to a png image called world.png the current directory
mapnik.render_to_file(m,'world.png', 'png')
# Exit the python interpreter
@ -108,21 +167,25 @@ start world.png
Or navigate to your base directory and open world.png and the result should look like this: ![world.png]
### Step 4
### Step 3
The next logical step is to run that same code all at once as a python script from your shell/terminal (rather than pasted into the python interpreter line-by-line). This way you will be able to modify and experiment with the settings, then simply re-run the script.
The next logical step is to run the same code as a python script from your shell/terminal. This way you will be able to modify and experiment with the settings.
So, create a blank text file called `world.py`.
This can be done by adding a line at the top of the script like:
Make it executable:
chmod +x world.py
Then add a line at the top of the script like:
```sh
#!/usr/bin/env python
```
Copy this entire text below and save as a file called world.py.
Finally, append the entire text below and save the file.
```python
#!/usr/bin/env python
import mapnik
m = mapnik.Map(600,300,"+proj=latlong +datum=WGS84")
@ -145,13 +208,6 @@ mapnik.render_to_file(m,'world.png', 'png')
* Note: Mapnik accepts both the absolute path to your data as well as the relative path
* Note: Same goes for the path to where you want to save your file
Next make the script executable. On Mac or Linux you would do this with the command:
```sh
chmod +x world.py
```
Finally run the script with the command: