freemapperDeutsch

Manual › freemapper-courseplan

8. Checking: reading findings, giving reasons for exceptions

check holds every individual and score course against the rules the event is meant to follow and tells you what stands out. It changes nothing in the project file. You decide whether to change the course or to give a reason for the finding as an intended exception.

freemapper-courseplan check hall.courseplan.toml
freemapper-courseplan check hall.courseplan.toml --all

check tests angles in both directions. It reports an acute angle as a hint below 90° and as a warning below 45°. If the course runs almost straight at a control, with an angle above 170° between the legs, it gives a hint (wkb2026.leg.straight_angle_hint). The control then hardly changes the running direction, and there is no room for the number behind the circle.

The program does not check team and relay courses yet. For each of them it reports once that it was not checked. Score courses have rules of their own (see Score courses below).

Reading a line

course A:
  hint course A, leg 36–37: the legs at control 37 form an angle of 85°, at least 90° required (wkb2026.leg.acute_angle_hint, BL12 5. Bahnkonzeption)

From left to right the line holds:

  • the severity, here "hint",
  • the place, here the leg from control 36 to control 37 of course A. A leg is named by its ends, such as "leg start–31" or "leg 37–finish"; as places, start and finish read "start S1" and "finish F1",
  • the text with the measured value and the limit,
  • in brackets the id of the rule and the source the limit comes from.

You need the id to give a reason for an exception. The source tells you where to read the rule yourself. Within a course the findings about the whole course come first, then those of its legs in running order. The output ends with one summary, in the example "0 errors, 0 warnings, 1 hint; 0 accepted, 6 skipped".

Severity and exit code

The rule profile sets how heavily a violation weighs for each rule. check itself treats the three levels differently only in the exit code:

  • Error: the profile treats the rule as binding. An open error sets exit code 1.
  • Warning: the profile expects you to change the course or give a reason for the deviation.
  • Hint: something stands out that you should look at, such as an angle just under the guideline.

The exit code tells a script how the run went. It is 0 when no error finding is open, 1 with at least one error or when the check is refused (project file or map unreadable), and 2 for a wrong call such as an unknown option. Warnings and hints do not change the exit code, and accepted findings do not count.

Some limits are not written in the rules like that; the program proposes them, for example that the halved control distances also apply to schoolyard maps. Their source then reads "proposal of freemapper-courseplan", and check --all shows the rule's note below the finding, which explains the proposal. The rule that the best running route must not lead through an out-of-bounds area needs the route analysis, which the program does not have yet. It shows in check --all among the rules that were not checked. A straight leg across an out-of-bounds area gets no finding, because the leg is the straight line and not the running route.

check does test where the controls stand. A control inside an out-of-bounds area is an error. The rule is wkb2026.control.in_forbidden, and on sprint and schoolyard maps wkb2026.control.in_forbidden_sprint. An area is a special object of the kind out-of-bounds area that belongs to the course of the control or names no course, and on the map any area with the symbol 520 or 709, on sprint and schoolyard maps also 714. A control exactly on the edge stands in the area. The rule does not test the start, the finish, crossing points or controls that are on no course. If you want the position anyway, accept the finding with a reason.

A start, finish, control or crossing point must also lie on the mapped area. Many maps are not rectangles: the Rummelsburger Bucht is a diagonal strip, and the rest of the sheet is blank, although it lies inside the rectangle around all map objects (which is what the warning wkb2026.map.edge measures against). A position in the blank part is an error, rule wkb2026.map.inside. A position counts as mapped when a map object lies within 50 m of it (on a forest map, within 10 mm on the sheet, see Forest maps below). The sheet frame and layout symbols such as the north lines pattern (601) do not count as objects. The message names the position in map millimetres and the nearest object. A square between buildings with no object in it stays inside as long as it is not much wider than 80 m; on a map with large empty areas, accept the finding with a reason or ask for a larger value in the rule (ADR 0084).

A start, finish, control or crossing point must also keep at least 50 m from the edge of the mapped area, so that the runner can always run on mapped ground (error, rule wkb2026.map.margin, ADR 0087). The edge is where no map object lies within 50 m any more, the same limit as above (on a forest map the forest limit). The distance is measured on a raster of the mapped area and is right to about 2 m. A position outside the mapped area gets only the error above. The setting is map_margin_m (0 to 200, 0 switches the rule off), changed with score settings --map-margin.

Two recommendations of the project owner, not rules of the WKB, apply to sprint and schoolyard maps. A control that stands deep in a dead end gives the runner no route choice, only one long leg in and out. check warns when a control lies more than 60 m deep in a dead end (rule wkb2026.control.dead_end, ADR 0085). The depth is the walk from the control to where the path network offers a choice again; a large open area or courtyard is no dead end. The limit is the setting dead_end_max_m (20 to 300, score settings --dead-end-max). Start and finish of a sprint usually lie close together: check gives a hint when they are more than 400 m apart in a straight line (rule wkb2026.course.start_finish_distance, ADR 0086, setting start_finish_max_m, 50 to 2000, score settings --start-finish-max). A course made by hand may deviate from all three; score suggest and score draft keep to them. score draft refuses a start and finish that are further apart or closer to the edge than the settings allow, and says which setting to change.

Forest maps

The program sets courses on forest maps made after ISOM 2017-2, at 1:5000 to 1:15000, with the same commands as on a sprint map. init reads the name of the map's symbol set. A forest map in ISOM 2017-2 is accepted. A forest map in ISOM2000, in the first edition ISOM2017 or in another set is refused, and the message names the set it found, because the rule data uses ISOM 2017-2 symbol numbers that mean something else in ISOM 2000 (ADR 0092). Convert such a map in OpenOrienteering Mapper first. Sprint and schoolyard maps work as before. The program takes the kind of the map from freemapper.toml if that names one, else from the symbol set and the scale; an ISOM map at 1:4000 counts as a sprint map.

On a forest map check uses the forest variants of the rules: at least 30 m between controls, similar features within 60 m (the groups are ISOM 2017-2 symbols, see the research file docs/research/2026-11-14-isom-2017-2-symbols.md) and a control in an area with the symbol 520 or 709 as an error. A symbol that is hidden in the map file does not count as map content, and the 709 symbol is hidden in Mapper's ISOM 2017-2 symbol set. The limit for the mapped area is a distance on the sheet, 10 mm: 50 m at 1:5000, 100 m at 1:10000 and 150 m at 1:15000. map.inside and map.margin use it; the setting map_margin_m keeps its 50 m.

Not everything is checked. The dead end rule and the distance between start and finish are recommendations for sprints; check --all lists them as "not for forest maps". The Lkm rules of the overlay nor-bb-2021 need the climb of the course, which the program cannot work out without an elevation model; enter it with course set --climb, or the rule stays skipped. The rule about the running route through out-of-bounds areas still waits for the route analysis.

score suggest, score draft and score length refuse a forest map, and so do the matching MCP tools and the Score panel of the editor, where the three buttons stay but are disabled. The route search works on a raster of the map symbols and knows neither elevation nor the speed through vegetation, so its times would mislead. You can still add a score course, its controls and links by hand. check then tests codes, values and links, and lists the length bands as not checked.

Skipped rules

check counts the skipped rules in its summary, and check --all names each with its reason. Common reasons:

  • At a training the program does not apply the length and difficulty rules of competitions.
  • The rule applies only to another discipline, map kind or print scale.
  • The rule needs the running route. wkb2026.leg.through_forbidden and wkb2026.leg.through_forbidden_sprint (the best route must not lead through an out-of-bounds area) wait for the route analysis, so they always show here.
  • An overlay switched the rule off; the reason names it, such as "rule profile nor-bb-2021 switches the rule off".
  • A value is missing. event start-window hall.courseplan.toml 90 sets the start window (--none instead of the minutes removes it), class set hall.courseplan.toml H21 --starters 40 the expected starters of a class, and course set hall.courseplan.toml A --climb 120 the climb of a course.
  • A class has no rule category, for example a name you chose yourself such as "Offen". class set hall.courseplan.toml Offen --rule-category H21 gives it one. A rule category in no row of the table skips the course too.

A skipped rule has not checked anything. An empty result therefore does not mean that everything passes. Read the list once before you rely on it.

Score courses

check also tests score courses. It does not apply the rules for individual courses to them (angles, control distances along legs); eight rules of its own read the control set, the limit and the links. All carry the id prefix wkb2026.score.:

  • value_code (error): the value of a control matches the value its code carries (chapter 3, Score courses).
  • code_ambiguity (warning): two codes of the course look alike upside down or differ only by 6 and 9, such as 61 and 91.
  • ring and order (warnings): the controls do not stand in a ring round start and finish, and their order is not obvious, for example because they lie on the outer hull and the nearest-control tour is almost the shortest. Both look at courses from six controls on.
  • length_bands (warning): on the cost surface of the map the good runner scores 87 to 93 percent of all points by default, and the very good runner takes every control and finishes after 95 to 100 percent of the limit. The bands are targets with a tolerance of 3 percentage points by default; only a miss beyond it is a finding, and a deviation within it only shows in the length report. Bands and tolerance are settings of the event. The rule searches routes and takes a few seconds on a large map. Without map objects, or if the search fails, it is skipped with the reason.
  • limit_penalty (warning): the limit lies between 30 and 90 minutes, and a minute over costs at least as many points as the highest control value.
  • link (error or warning): a link needs two different controls of the course and may appear once. A straight line across an out-of-bounds area or a forbidden route is a warning, and so is a link with no second route of similar length. The file has no flag for the second case; if it is intended, accept the hint with a note.
  • mix (warning): the share of hard controls matches the setting mix_hard_percent within 15 points. It judges from five controls on.

Several of these limits are proposals of freemapper-courseplan, not values from a source; the source in the line says which. A finding at a pair of controls is accepted with --controls, one about the whole course with --course, as in the examples examples/gleisdreieck/check.en.txt and examples/rummelsburger-bucht/check.en.txt. Team and relay courses stay unchecked.

Giving a reason for an exception

Some findings are intended. You accept them with the id of the rule, the place and a note:

# two controls that stand close together on purpose
freemapper-courseplan finding accept hall.courseplan.toml wkb2026.distance.min_half --controls 31,32 --note "The controls are on two sides of the same building and belong to different courses."

# a leg
freemapper-courseplan finding accept hall.courseplan.toml wkb2026.leg.acute_angle --leg A:3 --note "The sharp angle is intended, the course turns at this control."

# a control and the start
freemapper-courseplan finding accept hall.courseplan.toml wkb2026.distance.min_half --controls 31,S1 --note "The start is close to the first control on purpose, so the training begins with a short run-in."

--controls takes control codes and the id of a start or finish, and --course COURSE a finding about a whole course. --leg COURSE:N names the leg that leaves position N of the course. Positions count the controls of the course from 1, crossing points included, and 0 is the start. So N is not the control number on the map: on course A with controls 31 to 37, --leg A:6 is the leg 36–37. A finding at two legs gets --leg twice, such as --leg A:2 --leg A:5. A finding of the whole event needs no place. If you give a place where the rule never reports anything, the program refuses it and names the places the rule reports at. Accepting the same finding again replaces the old note with the new one.

The note is stored in the project file, for a leg together with its ends. check no longer lists accepted findings but counts them in the summary. With --all they appear under "accepted", with your note. That way next year's course setter can see why the course is the way it is.

When you change a course, an exception can lose its finding: the control was removed, the course deleted, or the leg now runs between other controls. In the last case check reports the finding at the new leg again, because the old note was about the old leg. check --all lists such exceptions under "not matched", the summary counts them, and they do not change the exit code.

To see a finding again, or to drop an exception that is not matched, take it back with the rule and the place:

freemapper-courseplan finding remove hall.courseplan.toml wkb2026.distance.min_half --controls 31,32
freemapper-courseplan finding remove hall.courseplan.toml wkb2026.leg.acute_angle --leg A:3

finding remove --leg first removes the exception for the leg as it runs now. If there is none, it removes the exceptions at that position whose leg has changed since. If the id of a rule changes, for example in a new program version, check reports its findings again.

Choosing the rule profile and overlays

An event has one base profile and any number of overlays on it. The program ships two: wkb-2026 (German competition rules 2026) and nor-bb-2021, an overlay with the course recommendations of Berlin and Brandenburg. One command sets both:

freemapper-courseplan event profile hall.courseplan.toml wkb-2026 --overlay nor-bb-2021

For several overlays, repeat --overlay; they apply in the order given. The program loads the profiles first and refuses an unknown name with the list of available profiles. info shows the profile. In every finding of check, the rule id starts with its profile (wkb2026, norbb2021). How a profile file is built and how to add one is described in crates/freemapper-courseplan/rules/README.en.md.

Date and organizer

event set stores the date of the event and its organizer in the project file:

freemapper-courseplan event set hall.courseplan.toml --date 2026-11-14 --organizer "OLV Example"
freemapper-courseplan event set hall.courseplan.toml --no-organizer

The date is YYYY-MM-DD, without a time of day. The organizer is a text that is not empty. Give at least one of --date, --organizer, --no-date and --no-organizer; --date together with --no-date is a usage error, and so are the two organizer options. info shows both. The IOF XML export writes them as Event/StartTime/Date and Event/Organiser/Name and leaves out a field that is not set (see "Handing on" below). freemapper-print takes them for the title block. Purple Pen has no field for either, so the Purple Pen export, import and sync do not touch them. The decision is ADR 0090.

With the AI assistant

The assistant has seven tools for this. check returns findings, accepted and unmatched exceptions and skipped rules. accept_finding and remove_accepted_finding match finding accept and finding remove, set_rule_profile matches event profile, set_start_window and set_class match event start-window and class set, and set_event_details matches event set. Ask it, for example, to check the courses, explain each finding in one sentence and ask you before it accepts one. You write the note or dictate it. The assistant should not invent one.

Version: v0.18.1 · Source on GitHub