Mkgmap/help/style rules

From OpenStreetMap Wiki
< Mkgmap‎ | help
Jump to navigation Jump to search
This page is outdated. Please refer to the style manual of mkgmap for up-to-date information.

Rules allow you to take a map feature in the OSM format, which uses a set of tags to describe the feature into the format required by Garmin maps, where features are identified by a number.

The rules for converting points, lines and polygons are held in correspondingly named files, as described in custom styles.

Each file contains a number of rules where you test the values of the tags of an OSM node or way and select a specific Garmin type based on the result of those tests.

See also: style examples.

Simple example

In the majority of cases everything is very simple. Say you want roads that are tagged as highway=motorway to have the Garmin type 0x01 ("motorway") and for it to appear up until the zoom level 3.

Then you would write the following rule.

highway=motorway [0x01 level 3]

Nodes that have an id and a subid are referenced by concatenating both ids.

amenity=bank [0x2f06 level 3]

This will be explained in more detail in the following sections along with how to use more than one tag to make the choice. However with that one form of rule, you can do everything that the old map-features file could do.


A rule is made up of two or three parts.

  • The first part is required: this is a set of tests that are performed on the tags of the item to be converted.
  • The second part is optional: this is the action block that can be used to do things with the tags of objects that match the tests and is contained in curly brackets {...}.
  • The third type is optional: this is the element type definition and sets the Garmin type and sometimes other parameters that will be used if the tests match. This part is contained in square brackets [...].

As a general point, space and newlines don't matter. There is no need to have rules all on the same line (although most of the examples here are shown in one line), and you can spread them out over several lines and add extra spaces wherever you like if it helps to make them easier to read. Here is an example of a rule containing all three sections:

natural=cliff { name '${name} cliff' | 'cliff' } 	[0x10501 resolution 22]

Include files

It is possible to import rules from external files. This is helpful to keep the style file simple, especially if you are using more than one style, or the same rule applied to more than one type (points, lines, polygons, relations). Popular examples are address rules, like in the default style:

include 'inc/address';

just copies the content of the external file 'inc/address' into this place. So, changes in address rules only have to be made in the include file.

Tag tests

The main test is that a particular OSM tag exists and has a given value (or key, in OSM parlance). So in the example above we had:


This means that we look up the highway tag in the OSM input file and if it exists and has the value 'motorway' then this test has matched.

You can also compare numeric quantities:

population > 10000
maxspeed >= 30
population < 1000000

Respectively, these mean: a population greater than ten thousand, a max speed greater than or equal to 30 and a population less than one million. You will be able to compare quantities that have units too, for example

maxspeed > 30mph

If a different unit is given in the tag (say km/h) then conversion will be performed. This is not implemented at the time of writing, so please let me know real useful cases that you come across that you would like to work.

You may also use regular expressions:

ele ~ '\d*00'

This checks whether ele is a multiple of 100.

Combining tag tests

Although it is possible to convert most OSM nodes and ways just using one tag, it is occasionally necessary to use more than one. In particular, for special purpose maps it is more likely that you will need to look at more than one tag.

For example, say you want to take roads that are tagged both as highway=unclassified and maxspeed=60 differently to roads that are just highway=unclassified. In this type of case, you might create two separate rules as follows:

highway=unclassified & maxspeed=60 [0x06]
highway=unclassified [0x05]

This means that roads that have both tags would use Garmin element type 0x06, whereas unclassified roads without would use type 0x05.

It is important to note that the order of the rules is important here. The rules are matched in the order that they occur in the style file and mkgmap stops trying to apply them after the first one that matches. If you had the rules above in the reverse order, then the highway=unclassified rule would match first to any OSM way with that tag/key pair, and the second rule would never get applied. Therefore, in general you want the most specific rules first and simpler, more general rules later on to catch the cases that are not caught by the more complex rules.

You can also combine alternatives into the one rule using a logical or, represented with a pipe (|) symbol. For example

highway=footway | highway=path [0x07]

This means if the road has either the highway=footway tag or the highway=path tags (or both), then the condition matches and mkgmap would use type 0x07 for the map. The logical or works exactly the same as if you had written two separate rules - one for footway and one for path - and indeed is converted to two separate rules internally as mkgmap runs.

You are not limited to two tests for a given rule... you can combine and group tests in almost whatever way you like (with one exception, see below). So for a slightly forced example the following would be possible:

place=town & (population > 1000000 | capital=true) | place=city

This would match if there was a place tag which had the value town and either the population was over a million or it was tagged a capital, or there was a place tag with the value city.

Important note

You must start each test with a simple test for tag equality or existence. Some attempt to re-arrange the expression to move an equality test to the beginning is made by mkgmap however.


Functions calculate a specific property of an OSM element.

Function Node Way Relation Description
length() x x Calculates the length in m. For relations its the sum of all member length (including sub relations).
is_complete() x true if all nodes of a way are contained in the tile. false if some nodes of the way are missing in the tile.
is_closed() x true the way is closed. false the way is not closed and cannot be processed as polygon.

The following rule matches for all service ways longer than 50m.

highway=service & length()>50 

Additional tests

To wrap up this section here are the other available tests.

highway!=motorway This will be true when the highway tag exists, but it does NOT have the value motorway, or when there is no highway tag at all.

highway=* This will be true whenever the highway tag exists. It doesn't matter what its value is. This is especially useful for tags where it is just their presence that matters.

highway!=* This rule will match only OSM objects that do not have a highway tag.

Action block

An action block is enclosed in braces {...} and contains one or more statements that can alter the element being displayed; multiple statements are separated by ;. When there is an action block, the element type definition is optional, but if used it must come after the action block.


This sets the final name of the element, that is, the name that will be used in the Garmin map. It is distinct from any 'name' tag on the element. You can give a list of alternatives separated by '|' pipe symbols. The first alternative that matches will be used. Once the name is set it cannot be overridden, so if more than one 'name' command matches then only the first to set the name will take effect.
Note: Since many objects can exist both as polygons and as points you should use the name command in both the points and polygons styles. Tags can be used in the value by using the notation: ${tagname}.

Example for roads:

{name '${name} (${ref})' | '${ref}' | '${name}'}

This means that if name='Main St' and ref=A1 the name would be "Main St (A1)", but if there was no 'name' tag then the result would be just "A1". If the name tag was set and not the ref tag, then the element-name would just be the value of the name tag.

For highway shields, you can use the notation ${tagname|highway-symbol:box}. Valid symbols are interstate, shield, round, hbox, box and oval.


The add command adds a tag if it does not already exist. The only tag where this is directly useful is the 'oneway' or 'name' tags. For example make motorways oneway unless the oneway tag is set:

highway=motorway { add oneway=yes }

The other use is in in relations with the 'apply' command.

All the same you can set any tag you want, it might be useful so you can match on it elsewhere in the rules.

You can also use substitutions.

 {set name='${ele}'; set name='${ref}';}

These two commands would set the 'name' tag to the value of the 'ele' tag if it exists, or to the value of the 'ref' tag if that exists.

You can also give a list of alternative expressions separated with a vertical bar in the same way as on the name command. The first one that is fully defined will be used.

 {set key123 = '${name:en}' | '${name}'; } 

Will set 'key123' to the value of the 'name:en' tag if it exists and to the 'name' tag if not.


The 'set' command is just like the 'add' command, except that it sets the tag, even if the tag already exists.


The delete command deletes a tag.

{ delete key123 }

The same result can be achieved by setting the value of the key to an empty string.

 {set key123 = '' } 


The "apply" action only makes sense in relations. Say you have a relation marking a bus route, but none of the ways that are in the relation have any special tags to indicate that they form part of that bus route, and you want to be able to tell from looking at the map which buses go where. You can write a rule in the relations file such as:

type=route & route=bus {
	apply {
		set route=bus;
		set route_ref='${route_ref}';

(note that "route_ref" is created already in the default style's "relations" file - it's set to the route "ref" or "name" depending on which exists)

Then in the lines file you will need to write a rule to match route=bus. All the relation rules are run before any others so that this works.

The substitution '${route_ref}' takes the value of the tag on the relation and applies it to each of the ways in the relation.

The substitution '$(route_ref)' can be used for adressing the value of the tag on the actually processed member of the relation, e.g.

type=route & route=bus {
	apply {
		set route=bus;
		set name='$(name) ${route_ref}';


The apply_once action is like apply, but it will apply the action once per relation member. A round-trip route relation may include the same ways multiple times, unless all member ways have been defined as parallel oneway streets.

The apply_once action is not useful for those cases when a bus route has been as two relations, one per direction, as the current recommended practice seems to be:

relation 195498
relation 1153657

Useful tags for routing and address search

For general routing, using avoid options, and address search, the use of some special tags is neccessary, see Mkgmap/help/Tags

Element type definition

As noted above this is contained in square brackets and if used must be the last part of the rule.

The first and only mandatory part of this section is the Garmin type code which must always be written in hexadecimal. Following this the element type definition rule can contain a number of optional keywords and values.

level (see also resolution)

This is the highest zoom level that this element should appear at (like EndLevel in the mp format). If the level for an object is not given then it defaults to 0 and so the specified feature will only appear at the most detailed level. You can, alternatively, use the resolution command to achieve the same outcome. In either case, the mapping between level and resolution is given in the options style file, where you will see something like this:

# The levels specification for this style
levels = 0:24, 1:23, 2:22, 3:20, 4:18, 5:16

This sets level zero equal to resolution 24, level 1 to resolution 23 and so on.

In the following example, we set highways to appear from zoom level 4 down to zoom level 0:

highway=motorway [0x01 level 4]

Level ranges

You can also give a range (e.g. 1-3) and the map will then contain the object only between the specified levels. Note that if you want to make use of this multi-level option, you must stipulate the lower level first (e.g. 1) and the higher level second (e.g. 3), for example:

highway=motorway [0x01 level 3-5]

In this example, motorways will appear at zoom level 5, which is most zoomed out, and continue to be visible until zoom level 3, which is moderately zoomed in, and then will not be shown in zoom levels 2, 1 and 0 (most zoomed-in).

resolution (see also level)

This is an alternative way of specifying the zoom level at which an object appears. It is specified as a number from 1-24, which corresponds to one of the zoom levels that Garmin hardware recognises. You should not use resolution if you have used level, but there is no difference between the two commands.

Resolution ranges

Just as with levels, you can specify a range of resolutions at which an object should appear. And just as with level ranges, you just stipulate the higher resolution (i.e. larger number) first and the lower resolution (i.e. lower number) second, for example resolution 23-20. Here's a complete example

highway=residential [0x06 resolution 22-16 continue]
highway=residential [0x07 resolution 24-23]

This example creates highways of type 0x06 between resolutions 16 and 22, then highways of type 0x07 between resolutions 23 and 24. This example makes use of the continue statement, which is discussed in more detail below.


If the element has not already had a name defined elsewhere in the rule, it will be given the name specified by default_name. This might be useful for things that usually don't have names and don't have a recognisable separate Garmin symbol. You could give a default name of 'bus stop' for example and all bus stops that didn't have their own name would now be labelled as such.


Used for routing; gives the class of the road where 4 is the best roads eg. motorways and 0 are residential roads etc. The classes are used as follows:

Class Used as
4 Major HW/Ramp
3 Principal HW
2 Arterial St / Other HW
1 Roundabout / Collector
0 Residential Street / Unpaved road / Trail


According to this Mail the speed limits are hardcoded in the mkgmap-code, so you don't have a care about it, but if you want to change it, you can do like this:

Used for routing; an indication of how fast traffic on the road is. 0 is the slowest and 7 the fastest. The speed limits that Garmin knows are shown in the following table:

road_speed highest speed
7 No speed limit
6 70 mph / 110 kmh
5 60 mph / 90 kmh
4 50 mph / 80 kmh
3 35 mph / 60 kmh
2 25 mph / 40 kmh
1 15 mph / 20 kmh
0 3 mph / 5 kmh


As discussed above, style rules are matched in the order that they occur in the style file. By default, for any given OSM object mkgmap will try each rule in turn until one rule with a element type definition matches; it will then stop trying to match further rules against the current OSM object. If the rule only has an action block mkgmap will continue to find other matches.

However, if you add a continue statement to the definition block of a rule, mkgmap will not stop processing the object but will instead carry on trying to match subsequent rules until it either runs out of rules or finds a matching rule that does not include a continue statement.

Note that by default when using the continue statement the action block of the rule (if there is one) will only be applied within this rule and not during any following rule matches. Use the continue with_actions statement if you want to change this behaviour (see next section).

continue with_actions

The with_actions statement modifies the continue behaviour in such a way, that the action block of this rule is also applied, when this element is checked for additional conversions.

Example of a full element type definition:

[0x2 road_class=3 road_speed=5 level 2 default_name 'example street' continue with_actions]

This feature can be used to show overlays for bridges, oneways,... or to show streets in different thickness depending on the zoom level.

Some examples

The following are some examples of style rules, with explanations of what they do.

Points style file

  • Internet cafes
amenity=cafe & internet_access=wlan {name '${name} (wifi)'} [0x2a14 resolution 23]

Checks to see if an OSM object has both the amenity=cafe and internet_access=wlan key/tag pairs. If name=Joe's Coffee Shop, then the Garmin object will be named Joe's Coffee Shop (wifi). The Garmin object used will be 0x2a14 and the object will only appear at resolutions 23 and 24

  • Guideposts
information=guidepost 	{name '${name} - ${operator} - ${description} '| '${name} - ${description}' | '${name}' | '${description}' | '${operator}' | '${ref}' } 							[0x4c02 resolution 23 default_name 'Infopost']

Checks to see if an OSM object has the information=guidepost key/tag pair.
If name=Route 7, operator=Kizomba National Parks and description=Trail signpost, the Garmin object will be named Route 7 - Kizomba National Parks - Trail signpost.
If the OSM object just has the name and description tags set, the Garmin object will be named Route 7 - Trail signpost
If just the name tag is available, the Garmin object will be named Route 7
If just the description tag is available, the Garmin object will be named Trail signpost; and
If just the operator tag is available, the Garmin object will be named Kizomba National Parks.
The Garmin object used will be 0x4c02 and will only appear at resolutions 23 and 24

  • Car salesrooms
shop=car {name '${name} (${operator})' | '${name}' |'${operator}'}	[0x2f07 resolution 23]

If name=Ali's Car Salesroom and operator=Nissan, the Garmin object will be named Ali's Car Salesroom (Nissan)

  • Opening hours
opening_hours=* {set addr:postcode = '${addr:postcode} open ${opening_hours}' | 'open ${opening_hours}'}

For any OSM object which has the opening_hours key set to a value, this sets the postcode to include the opening hours. For example, if addr:postcode=90210, addr:street=Alya Street, addr:city=Lagos and addr:housenumber=7 and opening_hours=09.00-17.00, the address field of the Garmin POI will be 7, Alya Street, Lagos, 90210 open 09.00-17.00.


For each node/way/relation, mkgmap goes through the tags exactly once and finds the earliest rule that matches. The only exception is if the continue statement is used. Rules with actions have the actions run when seen.

  • Where possible always have the same tag on the left. This will make things more predictable.
  • Always set made-up tag names if you want to also match on them later, rather than setting tags that might be used already.

Note on backward compatibility

If you convert an old map-features.csv file to the new style-rules files then you will get exactly the same output as you did with the old map-features file and it will run at the same speed. There is a script for doing this called in the scripts directory (requires python to run).

The following two sets of examples show how the map-features lines can be converted into identical rules.

OLD: point|amenity|bank|0x2f|0x06|21
NEW: amenity=bank [0x2f06 resolution 21]

OLD: polyline|highway|primary|0x04||19
NEW: highway=primary [0x04 resolution 19]

If you have both a map-features.csv file and the points, lines, polygons rule files, then the result is exactly the same as if you wrote the corresponding rules for each line in map-features as above and appended to the bottom of the rule files.