User:Moresby/Understanding Mapnik/CSS styling with Cascadenik

From OpenStreetMap Wiki
Jump to navigation Jump to search
Understanding Mapnik
A Mapnik tutorial
Starting with Python
Using XML and CSS
CartoCSS and PostGIS
260-text.png - map generated using Cascadenik

First published in 2010, Cascadenik was an early attempt to apply some of the concepts of Cascading Style Sheets (CSS) to Mapnik. A map design is specified using a CSS-type syntax, and Cascadenik converts this into the appropriate XML input for Mapnik. Development of Cascadenik ceased in March 2013, although the ideas behind it fundamentally influenced the design of CartoCSS.

For example, the following Cascadenik input files provides a pretty good version of our example map. This is 260-text.mml:

<?xml version='1.0'?>
<Map srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
  <Stylesheet src='260-text.mss'/>
  <Layer name='line_layer' class='road_lines road_labels'>
    <Datasource>    
      <Parameter name='file'>data-roads.csv</Parameter>
      <Parameter name='type'>csv</Parameter>
    </Datasource>
  </Layer>
  <Layer name='point_layer' class='town_points town_labels city_points city_labels'>
    <Datasource>
      <Parameter name='file'>data-places.csv</Parameter>
      <Parameter name='type'>csv</Parameter>
    </Datasource>   
  </Layer>
</Map>

And this is 260-text.mss:

/* Set background colour to 'ghostwhite'. */
Map {
  map-bgcolor:      #f8f8ff;
}     

/* Specify how to draw the road labels. */
.road_labels [type != 'rail'] name {
  text-face-name:   'DejaVu Sans Book';
  text-size:        10;
  text-fill:        #000;

  text-placement:   line;
  text-halo-radius: 2;
}

/* For rail, we have a wide dashed line. */
.road_lines [type = 'rail'] {
  line-color:       #000;
  line-width:       2;
  line-dasharray:   6, 2;
}

/* Roads are narrow, black lines. */
.road_lines [type = 'road'] {
  line-color:       #000;
  line-width:       1;
}

/* Main roads are wider, black lines. */
.road_lines [type = 'mainroad'] {
  line-color:       #000;
  line-width:       2;
}

/* Main roads are wide, light blue lines. */
.road_lines [type = 'motorway'] {
  line-color:       #add8e6;
  line-width:       4;
}

/* Towns are marked with a small circle. */
.town_points [type = 'town'] {
  point-file:       url('circle_red_8x8.png');
}

/* Towns names are drawn in black. */
.town_labels [type = 'town'] name {
  text-face-name:   'DejaVu Sans Book';
  text-size:        10;
  text-fill:        #000;

  text-halo-radius: 3;
  text-dy:          7;
}

/* Cities are marked with a larger circle. */
.city_points [type = 'city'] {
  point-file:       url('circle_red_16x16.png');
}

/* City names are drawn in red. */
.city_labels [type = 'city'] name {
  text-face-name:   'DejaVu Sans Book';
  text-size:        12;
  text-fill:        #f00;

  text-halo-radius: 3;
  text-dy:          11;
}

Save the first of these files as 260-text.mml and the second as 260-text.mss and run:

python cascadenik-compile.py 260-text.mml 260-text.xml

This will create the following Mapnik XML input file 260-text.xml:

<Map background-color="rgb(248,248,255)" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
  <Style filter-mode="first" name="line style 3">
    <Rule name="rule 1">
      <Filter>([type]='mainroad')</Filter>
      <LineSymbolizer stroke-width="2" />
    </Rule>
    <Rule name="rule 2">
      <Filter>([type]='motorway')</Filter>
      <LineSymbolizer stroke="rgb(173,216,230)" stroke-width="4" />
    </Rule>
    <Rule name="rule 3">
      <Filter>([type]='rail')</Filter>
      <LineSymbolizer stroke-dasharray="6, 2" stroke-width="2" />
    </Rule>
    <Rule name="rule 4">
      <Filter>([type]='road')</Filter>
      <LineSymbolizer />
    </Rule>
  </Style>
  <Style filter-mode="first" name="point style 15">
    <Rule name="rule 8">
      <Filter>([type]='city')</Filter>
      <PointSymbolizer file="/home/moresby/work/circle_red_16x16.png" />
    </Rule>
    <Rule name="rule 9">
      <Filter>([type]='town')</Filter>
      <PointSymbolizer file="/home/moresby/work/circle_red_8x8.png" />
    </Rule>
  </Style>
  <Style filter-mode="first" name="text style 14 (name)">
    <Rule name="rule 6">
      <Filter>([type]='city')</Filter>
      <TextSymbolizer clip="false" dy="11" face-name="DejaVu Sans Book" fill="rgb(255,0,0)" halo-radius="3" size="12">[name]</TextSymbolizer>
    </Rule>
    <Rule name="rule 7">
      <Filter>([type]='town')</Filter>
      <TextSymbolizer clip="false" dy="7" face-name="DejaVu Sans Book" halo-radius="3">[name]</TextSymbolizer>
    </Rule>
  </Style>
  <Style filter-mode="first" name="text style 6 (name)">
    <Rule name="rule 5">
      <Filter>not (([type]='rail'))</Filter>
      <TextSymbolizer clip="false" face-name="DejaVu Sans Book" halo-radius="2" placement="line">[name]</TextSymbolizer>
    </Rule>
  </Style>
  <Layer name="layer 8" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
    <StyleName>line style 3</StyleName>
    <StyleName>text style 6 (name)</StyleName>
    <Datasource>
      <Parameter name="file">/home/moresby/work/data-roads.csv</Parameter>
      <Parameter name="type">csv</Parameter>
    </Datasource>
  </Layer>
  <Layer name="layer 16" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
    <StyleName>text style 14 (name)</StyleName>
    <StyleName>point style 15</StyleName>
    <Datasource>
      <Parameter name="file">/home/moresby/work/data-places.csv</Parameter>
      <Parameter name="type">csv</Parameter>
    </Datasource>
  </Layer>
</Map>

Now use Mapnik to turn this into a map:

python generate-map.py 260-text.xml

You should see no error messages, and you should see a new file in your working directory called 260-text.png. This should be almost identical to the previous map we produced, as shown above.

External links