Skip to content

Element Kinds

An "element kind" is a set of rules that apply to tags to split objects into groups. Before the refactoring, some groups were made to non-intersect, but that can be untrue now. Each element kind has a string name, and a set of tag matching rules.

We have multiple pre-defined element kinds:

  • amenity: objects that are editable in the "Amenity" mode. Usually they have opening hours, and deliver some services. Almost always it's for money. Shops, offices, parcel points, ATMs go here.
  • needsCheck: default to the amenity kind, indicates if the object benefits from regular assessment. Manifests as a checkmark in the POI editor mode.
  • structure: a subset of amenities that usually exist for decades or centuries once built. Examples are schools, churches, and stadiums.
  • micro: objects in the micromapping mode. Defined as "not amenity", and with a list of allowed keys.
  • needsInfo: lists of important missing tags for micromapping-related objects.
  • building: basically building=*, except for building=entrance.
  • entrance: basically entrance=* or building=entrance.
  • address: an address node. It should have no other use (no "main tag"), and either addr:housenumber or addr:housename.
  • everything: every key-value combination by which objects are filtered from the downloaded OSM extract. A rule of thumb is, no linear feature tags, and no polygons that are usually too big to grasp from being there.
  • empty: no meaningful tags except source and note.
  • unknown: a nothing-kind that's returned when an object does not match other kinds.

Main Key

Before everything else, we need to clarify what "main key" means. Every Door works on an assumption that every object in OSM data represents one separate entity. This is not always correct (e.g. shops on buildings, or fences and school areas), but mostly checks out: it is exceptionally rare to find a shop and amenity on a single POI object.

To detect the main key, Every Door iterates over a list of keys, taking the first one found. So for a polygon with amenity=school and barrier=fence, it would choose the first one. It does consider lifecycle prefixes, so changing shop to disused:shop changes nothing with regard to the main key.

It is not possible to extend the main key list through a plugin, you would need to submit a pull request.

Now, some standard element kinds apply only to the main tag (key and value). Those are amenity, needsCheck, structure, micro, and everything. Others either don't care for an object type (like empty), or acknowledge an object can be multiple things at once (like building).

Structural Style

A simple ElementKind looks like this:

kinds:
  cycleBarrier:
    matcher:
      barrier:
        only: [cycle_barrier]

The cycleBarrier element kind matches only a single tag, barrier=cycle_barrier, and only as a main key. If an object has more important defining tag, like amenity=post_box, the matcher won't pick up the barrier tag.

To look for matches in every tag, you can set onMainKey: false for the element kind definition. As mentioned earlier, it is rarely needed, so you must know what you're doing.

The only other thing in a kind is a tag matcher. It is defined in the matcher key when we're defining a new element kind, or completely replacing an existing one. For updating an existing element kind, e.g. for adding a support for a new tag, use instead the update key:

kinds:
  everything:
    update:
      amenity:
        replace: true
        except:
          - parking
          - parking_entrance
          - loading_dock
          - waste_dump_site
          - waste_transfer_station

You might think, this looks exactly like the current definition of the amenity tag handling, but here we skipped the parking_space value, allowing for editing its properties.

Note that the editor won't touch linear features, so there is no point in enabling, for example, waterway=river or highway=residential.

Matchers

A tag matcher (the one you specify for an element kind) looks at tag keys, like amenity or man_made. It's the first layer of testing: if a key is not in its dictionary, it fails the check.

The definition for a matcher is a map with tag keys for keys, and matching rules for values. Those matching rules can have those keys:

  • only: a list of values that can match. If it's not empty, all other values or an absence of the key would fail the check.
  • except: a list of forbidden values. The check will fail if the tag is present, and its value is in this list.
  • when: a map of "value: tag matcher". It invokes a nested matcher when the tag has the given value. It allows for testing secondary tags, e.g. recycling_type=* for amenity=recycling.
  • replace: when redefining a matcher, this boolean flag (true by default) controls whether we're replacing all the rules, or updating the lists. Matchers in when get replaced in any case. Note that you cannot remove entries from only and except, only append to the lists. To remove, just copy a list without some entries, like we did above in the amenity=parking_space example.

Matching on the Main Key

If an element kind containing tag matcher works on main keys, the matcher can automatically accept an object when certain keys are present. A list of those keys goes into $good attribute. It looks like this:

kinds:
  amenityKind:
    matcher:
      "$good":
        - shop
        - craft
        - office
        - healthcare
        - club
      amenity:
        except: [parking, ...]
        when:
          recycling:
            recycling_type:
              only: [centre]

This example illustrates both keys that are considered amenities for every possible values, and a conditional amenity=recycling handling.

Missing Tags

There is also a $missing tag matcher list, which behaves weirdly. It passes only when at least one of the listed keys are missing. That is, if we have "$missing": [leaf_type, leaf_cycle], then the matched will pass only when the object does not have at least one of those two tags.

Only one element kind uses the $missing list, and it's needsInfo. The micromapping mode uses it to display a white dot on objects that miss some important information. You can add that indication to new types of objects (based on their main key) using this example of how it's done for bus stops and pedestrian crossings:

kinds:
  needsInfo:
    matcher:
      highway:
        crossing:
          when:
            "$missing": [crossing]
        bus_stop:
          when:
            "$missing":
              - bench
              - shelter

Simple Style

Since API 1.2

Learning the structures can be hard, so there is another way to define an element kind: through lists. The first three examples from the previous section can look like this:

kinds:
  cycleBarrier:
    - barrier=cycle_barrier
  everything:
    update:
      - amenity=parking
  amenityKind:
    - shop=*
    - craft=*
    - office=*
    - healthcare=*
    - club=*
    - amenity except: [parking]
    - amenity=recycling:
      - recycling_type=centre

Basically you list supported tags for the element kind as strings. For conditions and exclusions, you can also use sub-lists.

  • amenity=* means all amenity values for the main key.
  • amenity=school means only school value for the amenity key.
  • no amenity=parking means all values supported for the amenity key, except parking.
  • amenity only: [value1, value2] is shortcut option to avoid re-typing the key. It is equivalent to amenity=value1 and amenity=value2.
  • amenity except: [value3, value4] is the same shortcut for no amenity=value1 and no amenity=value2.
  • amenity=recycling: [recycling_type=centre] from the example is a nested condition, which matches recycling only when there is a recycling_type=centre tag on the object.
  • on all keys in the parent matcher is a special phrase to disable matching on the main key only. See above for an explanation.

Updating Existing Kinds

When you put your tag list under the update key, it does not replace, but updates an existing element kind. It would make sense to update amenity, structure, needsCheck, or everything kinds, but not others. Note that due to this style not supporting the missing key, you should not update this way the needsInfo matcher.

kinds:
  amenity:
    - update:
      - leisure=* # add to good keys
      - no club=* # remove from good keys
      - amenity=post_box # remove from exceptions
      - amenity except: atm # add to exceptions
      - leisure=playground # add to supported tags
      - no leisure=marina # remove from supported tags
    - tourism except: # completely replace this value matcher
      - attraction
      - viewpoint
      - artwork
      - picnic_site
      - camp_pitch
      - wilderness_hut
      - cabin
    - tourism=information:
      - information=office
      - information=visitor_centre
    - xmas:feature except: tree

Note that due to differences in the structures, some changes might go not as planned. Please report an issue when you encounter this.