Getting started with Maperitive
Introduction
OSM
Introduction to Maperitive
Maperitive is a tool for rendering maps…
Installation
You can download Maperitive from the official website.
To install on Windows you can simply extract the zip and run Maperitive.exe.
Building a Custom map
To create a custom theme, I will use Seattle, WA (US) as a reference point. I picked Seattle because it is a moderately sized city with a wide variety of transportation infrastructure like roads, highways, railways, bus routes, tram lines, a monorail system, ferry connections, multiple airports as well as bridges and tunnels. Additionally, there are major bodies of water, a river, mountain ranges, and national parks nearby.
This combination of varied infrastructure and natural features withing a relatively small geographic area will make it easer to adjust and test different configuration options (e.g., adjusting scales, labels or line-widths) without having to move the map view around too much.
Open Maperitive
Open Maperitive and move the map view to cover a wide area of Seattle.
Download OSM data
When you first open Maperitive, it renders the map using the OpenStreetMap pre-rendered tiles. This means it doesn’t yet have any geographical data loaded - the map shown on the screen is to help you navigate and see what is being selected.
To download raw data from OpenStreetMap, click “Map” in the toolbar and “Download OSM Data” from the dropdown.
When you use the “Download OSM Data” feature, Maperitive loads all the data into memory. If you close the application, all downloaded data will be lost. You can monitor the amount of RAM used (displayed in the counter at the bottom-right) as the data is downloaded.
Once Maperitive has finished downloading and processing the geographical data, you will notice a slight update to the map’s contents. This is because the downloaded contents of the map are now rendered using the default ruleset.
You can edit any ruleset by clicking “Tools” -> “Edit Rendering Rules” (this will open the current ruleset in the default text editor) or alternatively (recommended) you can find the rules in the ./Rules subdirectory of the Maperitive installation.
Creating a new ruleset
@todo
Update the Default.mrules to an empty file and then click “Map” -> “Reload rendering rules”. As you will see, nothing has changed on the map. That’s because the new ruleset has not rendered anything yet and we still have the “web map” source loaded.
In the bottom right corner of the screen, under the “Sources tab” click on the “star” icon next to “Web map” to disable it.
Once the map renders with a blank ruleset file, you will see a black or mostly white map with an outline of the sea.
Overview of Rulesets
A Maperitive rendering ruleset structure looks like this:
properties
....
features
points
...
lines
...
areas
...
rules
....
Properties
The Properties section defines various map attributes, such as the default font, text color, and more. Most of these settings are self-explanatory.
Features
The feature selectors define map features that can be grouped based on their properties:
- Points – A single point on the map (latitude, longitude). For example,
postbox: amenity=post_box. These correspond to nodes in OSM data. - Lines – An ordered list of two or more points, often representing roads, railways, bus routes, etc.
- Areas – Filled polygons that define regions on the map, bounded by three or more points.
Rules
@todo
Draw something
Add the following content to the ruleset (replace existing content) and reload the rules (ctrl + R).
properties
map-background-color: #000000
map-sea-color: #FF0000
features
areas
water: natural=water
rules
target: water
define
fill-color: #ffff00
draw: fill
Let’s break down the parts of this rule set.
The properties section defines some general rules for the map:
- the background color of the map is set to black
#000000 - the map sea color is set to red
#FF0000
In the features section:
- we define an area feature selector called water and have it select all areas in OSM data that have the natural equal to water.
The rules section defines how to render these features:
taget: watertells the maperitive that this rule applies to the feature selector called water- we then have a keyword
definewhich is just an opening block for the definitions - we define the fill color for the matching areas to be yellow
#ff0000 - and finally,
draw:filltells Maperitive to render the area by filling the polygon
This ruleset will render the following map.
Why Didn’t Maperitive Render the Sea Yellow?
We’ve defined a rule to fill all water with yellow, so why is the sea ignored on the map?
This happens because of how OpenStreetMap data is structured. Oceans and seas are not typically defined as polygons, since it would be impractical to create polygons for entire oceans.
Instead, Maperitive follows this rendering process:
- The world starts as sea by default, meaning the first layer fills the entire map with red (
map-sea-color: #ff0000). - Land areas (polygons) are drawn on top, filling them with the background color (black).
- Defined bodies of water (lakes, ponds, rivers) are added next, and these polygons are painted yellow.
Since the ocean is never explicitly defined as a “water polygon,” it remains unaffected by the rule that colors water areas yellow.
Setting more appropriate colors for water
Previously, we used red for the sea and yellow for water to illustrate how rendering works in Maperitive. Now, it’s time to use more appropriate colors to build a visually refined map.
Let’s use the following colors:
Updating the ruleset
Replace your existing ruleset with the following:
properties
map-background-color: #ededed
map-sea-color: #90D5FF
features
points
lines
areas
water: natural=water
rules
target: water
define
fill-color: #90D5FF
line-color: #90D5FF
draw: fill
draw: line
Changes and Their Effects:
map-background-color: #edededSets the land color to white (a shade darker than full white)map-sea-color: #90D5FFfills the sea with a very light blue
Water area rules:
fill-color: #90D5FF&draw: fill– Colors all water areas (lakes, rivers, ponds) with the same light blue as oceansline-color: #90D5FF&draw: line– Ensures the water border matches the fill color.- By default, Maperitive adds a black border around areas.
- Setting
line-colorto the same color as the area being filled ensures that no border is shown- I did not find a way to remove this default styling - setting:
line-width:0orline-style: nonedid not have any effect
- I did not find a way to remove this default styling - setting:
You may need to close Maperitive and restart it to have the changes apply (I’ve found it to be buggy and for some reason does not apply the background-change until restart).
Drawing Wooded Areas
To render forests and wooded regions, we define a feature selector that targets areas labeled with natural=wood or landuse=forest in OpenStreetMap data.
Feature Selector for Wooded Areas
areas
...
wood: natural=wood OR landuse=fores
This ensures that both natural woodlands and human-managed forests are included in the rendering.
Applying light green color to wooded areas
rules
...
target: wood
define
fill-color: #adebb3
line-color: #adebb3
draw: fill
draw: line
Add labels for cities and other localities.
In the previous step, we created a map displaying land, water, and wooded areas around Seattle. However, it’s still difficult to identify the city, its neighborhoods, and surrounding towns.
We’re now going to add labels to the map using point feature selectors.
Defining Point Feature Selectors
features
points
place-city: place=city
place-town: place=town
place-village: place=village
place-suburb-neighbourhood: place=suburb OR place=neighbourhood
lines
...
areas
...
The place key from OpenStreetMap defines locations known by specific names. We create selectors for cities, towns, villages, and a combined selector for suburbs and neighborhoods.
Drawing Labels on the Map
rules
target: place-*
define
text:name
draw:text
In this rule target: place-* applies the rule to all the feature selectors whose names start with place-.
The table below describes what each of those represent in OpenStreetMap.
| City | Largest urban settlements. Examples:
|
|---|---|
| Town | Smaller than a city, larger than a village.. Examples:
|
| Village |
A distinct small settlement. Examples:
|
| Suburb |
A part of a city or town that is known by a name. Suburb is usually on the outskirts of an urban area.
|
| Neighbourhood |
A part of a city, town or a suburb known by a name.
|
|
Definitions and what gets labelled as town vs. a suburb or neighbourhood can vary between locations and there can be differences in articles, wiki pages and maps.
|
|
Zoom levels
In the previous step, our map correctly labeled all cities, towns, suburbs, and neighborhoods in the Seattle metropolitan area. However, it’s still difficult to actually find Seattle.
This happens because all places are given equal importance at every zoom level. If we zoomed out to display the entire North American continent, the map would become cluttered with text, labeling everything from major cities to individual neighborhoods across the U.S., Canada, and Mexico.
In digital mapping, zoom levels define the scale at which the map is rendered - how much of the world is visible on the map and the details visible for a particular zoom level.
OpenStreetMap defines zoom levels from 0 - entire world to 20 - an individual house.
Updating the place labels to render based on zoom level
Let’s update our place-* rendering rule.
target: place-*
define
font-weight: bold
text:name
if: *city
define
font-size: 6:10;14:20
min-zoom: 6
max-zoom: 14
placement-value: 1000
elseif: *town
define
font-size: 9:8;12:10;14:15;20:20
min-zoom: 9
placement-value: 800
elseif: *village
define
font-size: 12:10;20:20
min-zoom: 12
placement-value: 600
elseif: *suburb*
define
font-size: 13:10;20:20
min-zoom: 13
placement-value: 200
draw:text
target: place-*applies the rule to all selectors starting withplace-(place-city, place-town, place-village, place-suburb-neighbourhood)- We first define common rendering rules for all places:
- set the font to bold
text:namesets the content of label to be the name attribute of the OSM node
We then selectively apply rules to every matched selector using if-else operators:
if: *city- from all the selectors matched intarget: place-*, apply these rules to those that end with “city”min-zoomandmax-zoom- only render when the map is zoomed between levels 6 and 14 (inclusive)placement-value: 1000- sets a high placement value for this label - on a busy map there could be multiple overlapping labels - in case of conflict the one with the higher placement label is renderedfont-size: at zoom level6the font-size is set to10em, at level14it is set to20em- intermediate values are calculated automatically
Rendering roads and streets
Now that we have labeled key locations on the map, it’s time to add roads and streets to provide structure and connectivity.
Feature selectors
features
points
...
lines
road-motorway: highway=motorway OR highway=motorway_link
road-trunk: highway=trunk OR highway=trunk_link
road-primary: highway=primary OR highway=primary_link
road-secondary: highway=secondary OR highway=secondary_link
road-tertiary: highway=tertiary OR highway=tertiary_link
road-unclassified: highway=unclassified
street-residential: highway=residential OR highway=living_street
road-service: highway=service
areas
...
In OpenStreetMap (OSM), roads and streets are represented as “ways” using the highway key. This key classifies roads into different types, from major highways and primary roads to local streets and pedestrian paths.
This classification helps determine how roads are rendered on the map, making highways and arterial roads more prominent, while having smaller streets only appear at higher zoom levels. By applying appropriate styling rules, we can control the visibility, color (although in this example we will use the same color for all roads), and thickness of roads to create a clear and intuitive map.
| highway=motorway | Restricted-access, high-speed roads, such as Interstates and freeways in the U.S., Autobahnen in Germany, motorways (M-prefix) in Ireland, and autostrade in Italy. |
|---|---|
| highway=motorway_link | Link roads or slip roads that connect one motorway to another, typically through various interchanges. |
| highway=trunk | High-importance roads that are not motorways, typically featuring multiple lanes and central dividers. N-prefix roads in Ireland for example. |
| highway=primary | Next most important roads, typically linking larger towns or cities. |
| highway=secondary | Typically connect smaller towns. |
| highway=tertiary | Connect smaller villages and towns. |
| highway=unclassified | The least important roads, typically found in rural areas. These roads serve purposes beyond property access and may or may not be paved. |
| highway=service | Alleyways, access roads to industrial sites and similar. |
| highway=residential | Streets and roads that provide access to residential areas and are not used as throughways. |
| highway=living_street | Roads in residential areas where pedestrians have priority over cars. |
Road rendering rules
rules
...
target: road-motorway
define
line-color: #ffffff
line-style: solid
min-zoom: 7
line-width : 7:1;9:2;12:3;13:7;16:10;18:12
draw: line
define
min-zoom: 14
text:name
draw: text
target: road-trunk
define
line-color: #ffffff
line-style: solid
min-zoom: 9
line-width : 9:2;12:3;13:7;16:10;18:12
draw: line
define
min-zoom: 14
text:name
draw: text
target: road-primary
define
line-color: #ffffff
line-style : solid
line-width: 10:0.8;9:1;12:3;13:4;14:8;16:10;18:12
min-zoom: 10
draw: line
define
min-zoom: 14
font-size: 14:8;16:11;18:13
text-halo-width : 15%
text-halo-opacity: 0.9
text:name
draw: text
target: road-secondary
define
line-color: #ffffff
line-style : solid
line-width : 12:0.8;13:1;14:4;16:8;18:10
min-zoom: 12
draw: line
define
min-zoom: 15
font-size: 15:6;16:10;18:10
text-halo-width : 0%
text-halo-opacity: 0.8
text:name
draw: text
target: road-tertiary
define
line-color: #ffffff
line-style : solid
line-width : 12:0.8;13:1;14:4;16:8;18:10
min-zoom: 12
draw: line
define
min-zoom: 15
font-size: 15:6;16:10;18:10
text-halo-width : 0%
text-halo-opacity: 0.8
text:name
draw: text
target: street-residential
define
line-color: #ffffff
line-style : solid
line-width : 12:0.8;13:1;14:4;16:8;18:10
min-zoom: 12
draw: line
define
min-zoom: 15
font-size: 15:6;16:10;18:10
text-halo-width : 0%
text-halo-opacity: 0.8
text:name
draw: text
target: road-service
define
line-color: #dddddd
line-style : solid
line-width : 14:1;18:7
min-zoom: 14
draw: line
Ferry routes
There are many ferry routes connecting Seattle to neighboring islands. Many people commute daily to work using ferries, making them an essential part of the region’s transport network.
Feature Selectors
To define ferry routes on our map, we can create a feature selector by specifying a line selector.
lines
...
ferry: route=ferry
Rules
target: ferry
define
line-color: #8080ff
line-style: dashdot
min-zoom: 9
line-width : 9:1;18:4
draw: line
define
min-zoom: 13
font-size: 13:11;18:16
text:name
draw:text
Adding operator information
In some locations, a single ferry route may be operated by multiple companies, each departing from different docks or areas within a city. Adding the operator name to the label might prove useful.
We can do this by changing the text:name line to text:name " (" operator ")".
The text: rule can be used to format the string that is being rendered. So far, we’ve only used the name attribute, but any attribute value can be used. The values in quotation marks are static text that will be rendered. A similar type of string concatenation in JavaScript would look like name + ' (' + operator + ')'.
Rendering the operator only when defined
If you look at the “Kingston Fast Ferry” line on the map, it looks like the operator for that route in not defined in OpenStreetMap which causes the text to be rendered with empty brackets as “Kingston Fast Ferry ()”. We can fix that by conditionally rendering the operator name only when it’s defined.
target: ferry
... line rules
define
min-zoom: 13
font-size: 13:11;18:16
for: operator
define
text:name " (" operator ")"
else
define
text:name
draw:text
The for keywords is used to apply the rules conditionally based on some properties of the feature being rendered. In this case we are checking for presence of the operator value and defaulting to just the name if it’s not defined. Javascript equivalent would be:
if (operator) {
label = name + ' (' + operator + ')'
}
else {
label = name
}
The boolean expressions in for can be used similarly to any programming language and can check for values too.
Example:
for: motorcar=yes and horse=no- if we wanted to apply a rule to a ferry line that transports cars but does not allow horsesfor: bicycle=yes or motorcar=yes- accepts either cars or bicycles
Let’s update the line rendering rule to make the line for passenger-only ferries slightly thinner and lighter color than the car ferries.
define
line-style: dashdot
min-zoom: 9
for: motor_vehicle=yes OR motorcar=yes
define
line-color: #5555bd
line-width: 9:1;18:4
else
define
line-color: #8080ff
line-width: 9:0.5;18:3
draw: line