TourBook import format
TourBook brings in tours, destinations, sector topos and challenge lists from
one documented file format — an import file, a JSON file named
*.tourbook.json — and this page describes it completely. Whatever you kept
your tours in (a spreadsheet, another app's export, a list of summits or a
guidebook's topo), it becomes such a file outside the app: by a spreadsheet
formula, a small script, or an AI given this page and the data. The app then
imports the file through the same path its tour form saves through, so an
imported tour is exactly what the same tour typed into the form would be.
This page is written for whoever produces the file. If you are an AI given this page and a spreadsheet: read the whole page, map every column you can to a field below, never invent a value the spreadsheet does not hold, and write one JSON file that passes the JSON Schema at the end of this page.
Workflow
- Write the file. Turn the spreadsheet (Excel, CSV, a pasted table) into a
*.tourbook.jsonfile following this format — by hand, by script, or by giving an AI this page and the spreadsheet. - Check it. In TourBook, More › Import data, choose the file. The app checks every value first and lists each problem with its JSON path (
$.tours[12].segments[0].sport), so a refused value can be fixed in the file and the file chosen again. Tours without a problem can be imported right away; the others are left out until fixed. - Import. Each tour is saved in its own transaction: a tour the app cannot save is reported with the reason, and the rest go on.
- Attach the tracks. More › Import tracks: the recordings of those days attach to the tours on the same date (a tour without a track is a match).
- Merge the leftovers. Settings › Tools › Data quality › Possible duplicates: merge the tours that now exist twice — for example a friend's list and your own logbook holding the same outing under different names.
Importing the same file again is safe: a tour is skipped when the logbook already holds an active tour on the same day with the same name (case and spacing ignored). Fix a refused tour in the file and import the whole file again; what came in the first time is not doubled.
File
An import file is one JSON object, UTF-8, of at most 32 MB:
{
"format": "tourbook-import",
"version": 1,
"tours": []
}
Every object in the format has a fixed set of fields, listed below.
A field that is not listed is refused, never ignored — a typo such as "gain" for
"gainM" is reported rather than silently dropped. A field that is optional
may be left out or set to null; both mean "not recorded".
Nothing is guessed. A value the app does not know — an unknown sport, a
grade a scale does not have, a field the sport does not record — refuses its
tour with a precise message. Map what you are sure of; leave out the rest, or
put it into the tour's description.
Fields
File
| Field | Type | Required | Meaning |
|---|---|---|---|
format | "tourbook-import" | yes | Names the format. |
version | 1 | yes | The version of this page. A file of another version is refused. |
tours | array of tours | one of five | The tours, in any order. |
topos | array of topos | one of five | Sector topos: routes without a tour (see Capturing a sector topo). |
routes | array of topo routes | one of five | Only on a sector's screen: routes into that sector, without naming it. |
locations | array of locations | one of five | Destinations without a tour: peaks, places, areas, sectors (see Capturing destinations). |
challengeLists | array of challenge lists | one of five | Lists of summits, huts or passes to tick off (see Capturing challenge lists). |
$schema | string | no | Ignored — room for a link to the schema. |
A file holds tours, topos, destinations, challenge lists, or several — at least one of the five lists. On a sector's or area's screen it holds neither tours nor destinations nor challenge lists.
Tour
One outing on one day. A multi-day trip is several tours (one per day) in one
collection.
| Field | Type | Required | Meaning |
|---|---|---|---|
date | "YYYY-MM-DD" | yes | The day, a real calendar date (2024-07-14). |
name | string | yes | The tour's title ("Piz Palü East summit"). Together with the date it identifies the tour for re-runs. |
description | string | no | Free text: notes, remarks, anything without a field of its own. |
region | region | no | Where the tour was: country, state, and/or a group of your own. |
collection | string | no | The collection the tour is filed under (a trip, a season, a project). Created when no collection of that name exists. |
partners | array of strings | no | Who was there, one name each (["Anna", "Beat Muster"]). A name the logbook knows is that person; a new name creates one. |
tags | array of strings | no | Tags, by name; new ones are created. |
segments | array of segments, at least one | yes | The sports of the day in order — a hike up and a flight down are two segments. |
Region
At least one of country, state and group. The country and state are the
administrative regions the app files tours under; the group is a region of
your own — a valley, a range, a ski area ("Saastal", "Berner Oberland").
Give the ISO 3166 codes beside the names whenever you know them —
"countryCode": "CH", "stateCode": "CH-VS". A country or state is
identified by its code: with it, "Wallis", "Valais" and "Vallese" are
the one canton the logbook and the installed region packs already know, and the
name only labels a region the logbook does not hold yet. Without a code the
name decides — use the names the logbook already uses ("Schweiz",
"Wallis").
| Field | Type | Required | Meaning |
|---|---|---|---|
country | string | no | The country ("Schweiz"). |
countryCode | "CH" | no | The country's ISO 3166-1 alpha-2 code, in capitals; only beside country. |
state | string | no | The state, canton or province inside it ("Wallis"). |
stateCode | "CH-VS" | no | The state's ISO 3166-2 code, in capitals; only beside state, and beginning with the countryCode when both are given. |
group | string | no | A group of your own; created when none of that name exists. |
A tour's region also decides which peaks, places, areas and sectors its names mean (see Finding known places): two peaks called "Schwarzhorn" in different states stay two peaks. Give the region whenever the spreadsheet says where a tour was.
Segment
One sport within the tour. Which fields a segment may carry depends on its sport — see Sports; a field the sport does not record is refused.
| Field | Type | Required | Meaning |
|---|---|---|---|
sport | sport key | yes | One key from Sports ("hiking", "climbing", …). |
name | string | no | A label for the segment ("Approach", "Flight down"). |
distanceKm | number from 0 to 1,000 | no | Distance in kilometres (12.5). |
gainM | number from 0 to 15,000 | no | Height gained in metres (1100). |
lossM | number from 0 to 15,000 | no | Height lost in metres. |
duration | "h:mm", at most "48:00" | no | Time taken, hours and minutes ("5:30"; "5:30:15" with seconds). A decimal such as 5.5 is refused — write "5:30". A longer outing is several tours. |
grade | grades | no | The day's own difficulty, only on the sports marked segment grade ({ "hiking": "T3" }). Technical sports grade their routes instead. |
peaks | array of peaks | no | Summits of this segment. |
places | array of places | no | Huts, passes, trailheads, villages. |
routes | array of routes | no | The routes climbed, skied or walked — with their grades. |
Peak
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The summit's name ("Piz Palü"). |
lat | number | no | Latitude in degrees (WGS84), with lon. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
outcome | "reached" or "turned_back" | no | Whether the summit was reached. Left out, the routes at the peak decide (a route turned back from makes the peak "turned back"), else "reached". |
A peak the logbook or an installed region pack already knows is that peak (see Finding known places); its position and height stay its own. The file's position and height are written only on a peak new to both — and the position is what tells two summits of one name apart.
Place
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The place's name ("Diavolezza", "Cabane de Moiry"). |
lat | number | no | Latitude in degrees, with lon. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
Route
A route visited on the segment: where it lies, its grade, and how it went.
Where it lies is exactly one of peak, place or sector (a sector with
its area if known, a bouldering block with its sector and boulder), of a
kind the sport hangs routes off (routes on in Sports).
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The route's name ("Normal route", "Motörhead"). |
peak | string | one of three | The summit the route leads to. |
place | string | one of three | A place the route starts from or leads to (a gorge, a ski area). |
area | string | no | The climbing area a sector lies in ("Gadmen"). |
sector | string | one of three | The crag sector ("Wendenstöcke"); for bouldering the boulder sector. |
boulder | string | bouldering | The block a problem is on, inside its sector. |
grades | grades | no | The route's grade per scale ({ "alpine": "ZS", "uiaa": "III" }). |
pitches | array of pitches | no | Pitch by pitch, on sports with pitches. |
style | style | no | How it was climbed (see Visit answers), on the style sports. |
role | role | no | Led, followed or alternated, on the rope sports. |
attempts | whole number from 1 to 999 | no | Goes needed, on the style sports (a project that took 12 tries). |
outcome | outcome | no | Reached or turned back, on the outcome sports. |
A route already in the logbook at that location, with that name and
sport, is that route: the visit is added to it and its grades and pitches stay
as the logbook has them. A new route is created from the file's grades and
pitches. A sector with its area is found by name inside that area (the
area itself by name in the tour's region); a sector named without its area
is the logbook's sector of that name and discipline in the tour's region —
or, without a region, the only one of that name anywhere — and else a new
sector without an area. Give the area whenever the spreadsheet has it.
Left out, the visit answers take the form's defaults: led, onsight on a
route never climbed before (flash for a boulder), redpoint on one tried
but not sent, repeat on one sent before; reached on the outcome sports.
Pitch
On sports with pitches. For a new route the pitches are its topo, in order; for a route already in the logbook the file must list as many pitches as the logbook holds (the visit's role and style per pitch), or the tour is refused.
| Field | Type | Required | Meaning |
|---|---|---|---|
grades | grades | no | The pitch's grade ({ "french": "6b" }), in a rock, ice or mixed scale. |
lengthM | number from 0 to 500 | no | The pitch's length in metres. |
role | role | no | This pitch's role, when it differs from the route's. |
style | style | no | This pitch's style, when it differs from the route's. |
A climbing route's grade is its hardest pitch's, and its length the sum of the
pitch lengths. A single-pitch route needs no pitches: its grades are pitch 1.
Topo
One sector's topo: where it is and its routes — no tour, no visit. A rope
sector lists routes, a boulder sector its boulders (blocks) with their
problems; the other list is refused.
| Field | Type | Required | Meaning |
|---|---|---|---|
area | topo area | in the data import | The climbing area the sector lies in. On a sector's or area's screen it may be left out; given, it must be that area. |
sector | topo sector | except on a sector's screen | The sector. On a sector's screen it may be left out; given, it must be that sector. |
routes | array of topo routes | rope sector | The routes, at least one. |
boulders | array of topo blocks | boulder sector | The blocks, at least one. |
Topo area
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The area's name ("Balmfluh"). Found by name in its region (see Finding known places), else created. |
lat | number | no | Latitude in degrees, with lon. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
region | region | no | Country and state the area lies in, and a group of your own that holds it. Without a region the area is found only when its name is unique in the logbook. |
Topo sector
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The sector's name ("Hauptwand"). Found by name inside its area, else created. |
discipline | "rope" or "boulder" | no | A rope sector (the default) or a boulder sector. |
lat | number | no | Latitude in degrees, with lon. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
Topo block
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The block's name ("Ei"). Found by name inside its sector, else created. |
lat | number | no | Latitude in degrees, with lon. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
routes | array of topo routes | yes | The problems on the block, at least one. |
Topo route
A route as the topo describes it. Which fields it may carry depends on its sport, as on a tour's route.
| Field | Type | Required | Meaning |
|---|---|---|---|
topoIndex | whole number from 0 to 9,999 | no | The route's number on the topo or the wall (some topos start at 0). The sector lists its routes by it, then the unnumbered ones by name. |
name | string | yes | The route's name ("Bazillus"). |
sport | sport key | no | climbing on a rope sector and bouldering on a block when left out; ice_climbing and mixed_climbing also lie on a rope sector. |
grades | grades | no | The route's grade ({ "french": "7b" }). |
lengthM | number from 0 to 3,000 | no | The route's length in metres ("15m" → 15). |
pitches | array of topo pitches | no | A multi-pitch route's pitches in order; left out, the route is one pitch. Pitches without a grade leave the route's grades as its grade until a pitch is graded. |
character | "plaisir", "partly_bolted", "trad" or "alpine" | no | How it is protected, on sport climbing: well bolted, partly bolted, own gear, alpine. Left out, a new route takes plaisir. |
note | string | no | The topo's text: description, first ascent, "extension of …". |
Topo pitch
| Field | Type | Required | Meaning |
|---|---|---|---|
grades | grades | no | The pitch's grade ({ "french": "6b" }). |
lengthM | number from 0 to 500 | no | The pitch's length in metres. |
Location
A destination without a tour (see Capturing destinations).
| Field | Type | Required | Meaning |
|---|---|---|---|
kind | "peak", "place", "area" or "sector" | yes | What it is: a summit, a hut, pass or village, a climbing area, a crag sector. |
name | string | yes | Its name ("Weissmies"). |
area | string | no | Sectors only: the area the sector lies in ("Balmfluh"). Left out, the sector stands alone. |
discipline | "rope" or "boulder" | no | Sectors only: a rope sector (the default) or a boulder sector. |
lat | number | no | Latitude in degrees, with lon. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
region | region | no | Country and state it lies in, with their ISO codes, and a group of your own that holds it. |
Challenge list
A list of your own to tick off (see Capturing challenge lists).
| Field | Type | Required | Meaning |
|---|---|---|---|
key | string of a–z, 0–9 and - | yes | The list's identifier ("glarner-gipfel"). A list with the key of one of your lists replaces it; the key of a list that comes with the app is refused. |
name | string | yes | Its name as the challenges tab shows it ("Glarner Gipfel"). |
targetKind | "peak", "hut" or "pass" | yes | What its entries are: summits, huts or passes. |
source | string | no | Where the list comes from (a book, a website). |
version | string | no | The list's own version ("2026"). |
targets | array of challenge targets | yes | The entries, at least one, in the order the tab lists them. |
Challenge target
One entry of a challenge list. A name and region code appear at most once per
list — unless every entry of that name and code has lat and lon and they
lie more than 1000 m apart: then they are namesakes, like the three "Schijen"
of Uri, and each is ticked by a location near it.
| Field | Type | Required | Meaning |
|---|---|---|---|
name | string | yes | The summit's, hut's or pass's name ("Tödi"). A tour reaching a location of this name ticks it — a peak on a list of peaks, a place on a list of huts or passes. |
lat | number | no | Latitude in degrees, with lon — places the entry on the challenge's map, and tells namesakes apart: where several entries or several of your locations share a name, a placed location ticks only the nearest entry within 1000 m. |
lon | number | no | Longitude in degrees, with lat. |
elevationM | number from −500 to 9,000 | no | Height in metres. |
regionName | string | no | Shown beside the entry ("Glarus"). |
regionCode | ISO 3166 code | no | The country ("CH") or state ("CH-GL") the entry stands for, in a list of one per region. |
aliases | array of strings | no | Other names a tour may give the place (["Piz Russein"]). |
Grades
A grades object maps a scale to a grade token of that scale:
{ "french": "6a+" }, { "alpine": "ZS-", "uiaa": "IV" }. Scales of one
family (French, UIAA, YDS and Norwegian are all rock; Font and V are boulder) are
one grade — give one of them, the one the spreadsheet uses. A token must be
written exactly as in the table below (Font and V also accept lower case);
anything else is refused. A sport accepts the scales listed for it in
Sports.
Convert nothing yourself: a grade written in UIAA stays UIAA in the file, and the app shows it in whichever scale the user reads.
Grade scales
| Scale | Family | Tokens |
|---|---|---|
french | rock | 1 2 3a 3b 3c 4a 4b 4c 5a 5a+ 5b 5b+ 5c 5c+ 6a 6a+ 6b 6b+ 6c 6c+ 7a 7a+ 7b 7b+ 7c 7c+ 8a 8a+ 8b 8b+ 8c 8c+ 9a 9a+ 9b 9b+ 9c |
uiaa | rock | I II II+ III- III III+ IV- IV IV+ V- V V+ VI- VI VI+ VII- VII VII+ VIII- VIII VIII+ IX- IX IX+ X- X X+ XI- XI XI+ XII- XII |
yds | rock | 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10a 5.10b 5.10c 5.10d 5.11a 5.11b 5.11c 5.11d 5.12a 5.12b 5.12c 5.12d 5.13a 5.13b 5.13c 5.13d 5.14a 5.14b 5.14c 5.14d 5.15a 5.15b 5.15c 5.15d |
norwegian | rock | 1- 1 1+ 2- 2 2+ 3- 3 3+ 4- 4 4+ 5- 5 5+ 6- 6 6+ 7- 7 7+ 8- 8 8+ 9- 9 9+ 10- 10 10+ 11- 11 11+ |
font | boulder | 3 4 4+ 5 5+ 6A 6A+ 6B 6B+ 6C 6C+ 7A 7A+ 7B 7B+ 7C 7C+ 8A 8A+ 8B 8B+ 8C 8C+ 9A |
vscale | boulder | VB V0 V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 |
ice | ice | WI1 WI2 WI3 WI3+ WI4 WI4+ WI5 WI5+ WI6 WI6+ WI7 WI7+ |
alpine | alpine | L WS- WS WS+ ZS- ZS ZS+ S- S S+ SS- SS SS+ AS EX |
mixed | mixed | M1 M2 M3 M4 M5 M6 M7 M8 M9 M10 M11 M12 |
hiking | hiking | T1 T2 T3 T4 T4+ T5 T5+ T6 |
mtb | mtb | S0 S1 S2 S3 S4 S5 |
via_ferrata | via_ferrata | K1 K2 K3 K4 K5 K6 |
snowshoe | snowshoe | WT1 WT2 WT3 WT4 WT5 WT6 |
The SAC alpine scale is written with its German letters: L easy, WS
moderately difficult, ZS fairly difficult, S difficult, SS very
difficult, AS extremely difficult, EX excessively difficult (French F,
PD, AD, D, TD, ED, ABO map to them in that order).
Sports
What each sport records. Metrics are the segment fields it takes,
destinations whether its segments list peaks and places, routes on the
location kinds its routes hang off, pitches whether its routes have them
(required: every route is at least one pitch), visit what a route visit
answers, scales the grade scales it accepts, segment grade whether the
segment itself takes a grade.
| Key | Name | Metrics | Destinations | Routes on | Pitches | Visit | Scales | Segment grade |
|---|---|---|---|---|---|---|---|---|
hiking | Hiking | distanceKm gainM lossM duration | peaks places | peak, place | — | outcome | hiking | yes |
scrambling | Scrambling | distanceKm gainM lossM duration | peaks places | peak, place | — | outcome | hiking french uiaa yds norwegian | — |
alpine | Mountaineering | distanceKm gainM lossM duration | peaks places | peak, place | optional | outcome | alpine french uiaa yds norwegian | — |
climbing | Climbing | distanceKm gainM lossM duration | peaks places | sector, peak | required | style role attempts | french uiaa yds norwegian | — |
ski_touring | Ski Touring | distanceKm gainM lossM duration | peaks places | peak, place | — | outcome | alpine | — |
xc_skiing | Cross-Country Skiing | distanceKm gainM lossM duration | places | place | — | — | — | — |
running | Running | distanceKm gainM lossM duration | places | place | — | — | — | — |
orienteering | Orienteering | distanceKm gainM lossM duration | places | place | — | — | — | — |
mtb | Mountain Biking | distanceKm gainM lossM duration | peaks places | place, peak | — | outcome | mtb | yes |
cycling | Cycling | distanceKm gainM lossM duration | places | place | — | — | — | — |
trail_running | Trail Running | distanceKm gainM lossM duration | peaks places | peak, place | — | outcome | hiking | yes |
ice_climbing | Ice Climbing | distanceKm gainM lossM duration | peaks places | sector, peak | required | style role attempts | ice | — |
mixed_climbing | Mixed Climbing | distanceKm gainM lossM duration | peaks places | sector, peak | required | style role attempts | mixed ice | — |
paragliding | Paragliding | distanceKm gainM lossM duration | peaks places | place, peak | — | — | — | — |
bouldering | Bouldering | distanceKm gainM lossM duration | places | boulder | — | style attempts | font vscale | — |
ski_mountaineering | Ski Mountaineering | distanceKm gainM lossM duration | peaks places | peak, place | optional | outcome | alpine french uiaa yds norwegian | — |
via_ferrata | Via Ferrata | distanceKm gainM lossM duration | peaks places | peak, place | — | outcome | via_ferrata | — |
snowshoeing | Snowshoeing | distanceKm gainM lossM duration | peaks places | peak, place | — | outcome | snowshoe | yes |
Choosing the sport: a glacier tour or a high summit on foot is alpine
(mountaineering); a ski tour to a high summit, roped where it gets steep,
ski_mountaineering; a plain ski tour ski_touring; sport and multi-pitch
rock routes climbing; T-graded walks hiking, T4–T6 terrain with hands on
rock scrambling. Indoor climbing has no sport — leave it out or ask.
Riding on road, gravel or a cycle path is cycling; on trails, mtb. A file
written before 0.1.0 may still say road_cycling: it is read as cycling,
but write cycling.
Visit answers
Styles
On the style sports (style in the visit column), the route's and each
pitch's style. Bouldering takes flash, redpoint, repeat, attempt and
onsight only.
| Style | Meaning |
|---|---|
onsight | First go, no prior information, no fall. |
flash | First go with prior information (beta), no fall. |
redpoint | Led free after earlier tries. |
repeat | Climbed free again, sent before. |
french_free | Pulled on gear or bolts in places (A0). |
aid | Climbed on gear. |
attempt | Not topped out — a fall-off, a retreat. |
Roles
On the rope sports, the route's role and each pitch's.
| Role | Meaning |
|---|---|
lead | Led every pitch (the default). |
follow | Seconded. |
alternating | Swung leads. |
Outcomes
On the outcome sports, a route's outcome; on every sport with peaks, a
peak's outcome.
| Outcome | Meaning |
|---|---|
reached | Got to the top or the end (the default). |
turned_back | Turned back before it. |
Values
- Dates are
YYYY-MM-DD— convert14.07.2024,7/14/24and Excel serial numbers (45487is 14 July 2024) to2024-07-14. - Numbers are JSON numbers with a decimal point:
12.5, not"12,5"and not"12.5 km". Strip units and thousands separators (1'100 m→1100). Convert miles to kilometres and feet to metres. - Bounds. Every number has a plausible range, given in its field's type (a day's distance up to 1,000 km, its gain and loss up to 15,000 m, its duration up to 48 hours, a height from −500 to 9,000 m, a route up to 3,000 m, a pitch up to 500 m). A value outside it is refused with the range — almost always a unit mixed up: metres written as kilometres, feet as metres, minutes as hours.
- Durations are
"h:mm"strings:"5:30","0:45","12:05". A time written as5.5 his"5:30";330 minis"5:30". - Text is trimmed; an empty text is "not recorded".
- Names (partners, tags, peaks, places, sectors, routes, collections) are matched to what the logbook holds by their spelling, ignoring case and extra spaces —
"Piz Palü"and"piz palü"are one,"Piz Palu"is another. Keep one spelling per thing throughout the file.
What the import does
- Each tour is saved like a tour entered in the form: partners, tags and the collection by name (created when new), the region as country → state plus a group, peaks, places, areas and loose sectors as described under Finding known places (created when nothing is known, with the file's position and height), sectors within their area, blocks within their sector, routes by name at their location and sport (created when new, from the file's grades and pitches), and every visit with its answers. The statistics, challenge lists and grade overviews see an imported tour as any other.
- One transaction per tour: a tour either comes in whole — with every location and route it created — or not at all. The import reports each tour as imported, already present, or failed with its reason, and goes on after a failure.
- No network: nothing is looked up online during the import — only the region packs installed on the device. A new peak with no position stays without one; data care (Settings › Tools › Data quality) later proposes positions, heights and regions from the installed packs.
- Already present — same day, same name, not in the trash — is skipped.
- A sector of the other discipline (a rope sector named where a bouldering route needs a boulder sector of the same name) refuses its tour.
Finding known places
A peak, place, area or loose sector named in the file is matched to what the logbook already holds — **including the peaks and places it took from a region pack**, which most picked ones are — by its name (case and spacing ignored) and kind:
- With a country or state in the region: the logbook's rows of that name in it or in a state under it — filed there, placed there by their position, or holding a sector that is. None there: a row nothing places in any region at all (an old area without a region or positions). With only a group: the rows in the group, else every row. Without a region: every row.
- One is that row. Several: the one within 200 m of the file's
lat/lon; else — with a region — the one filed most precisely under it (the region itself before a state under it before one placed only through its sectors); else the tour (or topo) is refused as ambiguous, saying how many there are: addlatandlon(or the region) in the file. - None in the logbook (peaks and places): the installed region packs, by name, in the file's state or country (everywhere without one) — one feature becomes the logbook's peak with the pack's position and height; several are told apart by
lat/lonas above, else refused as ambiguous. - Nothing known: a new row, filed under the tour's region.
Mapping a spreadsheet
Typical columns and where they go (the column names are examples — German logbooks often use the ones on the left):
| Column | Field |
|---|---|
| Datum, Date | date |
| Tour, Ziel, Name, Titel | name |
| Sportart, Aktivität, Typ | segments[].sport — map each value to a key from Sports (Wandern → hiking, Hochtour → alpine, Skitour → ski_touring, Skihochtour → ski_mountaineering, Klettern → climbing, Bouldern → bouldering, Klettersteig → via_ferrata, Schneeschuh → snowshoeing, Bike/MTB → mtb, Velo/Rennrad/Radfahren → cycling, Laufen/Joggen → running, Trailrunning → trail_running, Langlauf → xc_skiing, Gleitschirm → paragliding, Eisklettern → ice_climbing) |
| Partner, Begleitung, Mit | partners — split "Anna, Beat & Carla" into three names |
| Gipfel, Berg | segments[].peaks[].name |
| Hütte, Ort, Pass, Start | segments[].places[].name |
| Höhenmeter, Aufstieg, Hm ↑ | segments[].gainM |
| Abstieg, Hm ↓ | segments[].lossM |
| Distanz, Strecke, km | segments[].distanceKm |
| Zeit, Dauer, Gehzeit | segments[].duration as "h:mm" |
| Schwierigkeit, Grad, Bewertung | a route's grades (or the segment's grade on hiking, trail running, MTB, snowshoeing) with the scale the value is written in: T3 → hiking, ZS → alpine, 6a → french, V+ → uiaa, WT2 → snowshoe, K3 → via_ferrata |
| Route, Weg, Linie | segments[].routes[].name, on its peak or sector |
| Gebiet, Klettergebiet | a route's area |
| Sektor, Wand | a route's sector |
| Stil, Begehung | a route's style (OS → onsight, Flash → flash, RP/Rotpunkt → redpoint, AF/A0 → french_free, Versuch → attempt) |
| Seillängen | the number of pitches (with their grades when listed) |
| Gipfel erreicht? Abbruch, Umkehr | a peak's outcome (turned_back for an abandoned summit) |
| Region, Kanton, Land, Gebiet | region (country, state with their ISO codes countryCode, stateCode; a valley or range as group) |
| Bemerkung, Notizen, Kommentar | description |
| Saison, Reise, Projekt | collection |
| Stichworte, Labels | tags |
A row with two sports (hike in, climb, walk out; ski up, fly down) is one tour with several segments. Two rows on the same day for one outing are one tour; two outings on one day are two tours with different names. When a row does not have a name, build one from what it has ("Pizol", "Pizol – Wildsee"), since the name identifies the tour on a re-run.
Example
{
"format": "tourbook-import",
"version": 1,
"tours": [
{
"date": "2024-06-02",
"name": "Pizol – Five lakes",
"description": "Hot day, the upper lakes still half frozen.",
"region": { "country": "Schweiz", "countryCode": "CH", "state": "St. Gallen", "stateCode": "CH-SG" },
"partners": ["Anna", "Beat"],
"tags": ["Family"],
"segments": [
{
"sport": "hiking",
"distanceKm": 12.5,
"gainM": 1100,
"lossM": 1100,
"duration": "5:30",
"grade": { "hiking": "T3" },
"peaks": [{ "name": "Pizol", "lat": 46.9586, "lon": 9.3897, "elevationM": 2844 }],
"places": [{ "name": "Wangs" }]
}
]
},
{
"date": "2024-07-20",
"name": "Weissmies",
"region": { "country": "Schweiz", "countryCode": "CH", "state": "Wallis", "stateCode": "CH-VS", "group": "Saastal" },
"collection": "Hochtourenwoche 2024",
"partners": ["Beat"],
"segments": [
{
"sport": "alpine",
"gainM": 1350,
"lossM": 1350,
"duration": "7:15",
"places": [{ "name": "Hohsaas" }],
"routes": [{ "name": "Normal route", "peak": "Weissmies", "grades": { "alpine": "WS" }, "outcome": "reached" }]
}
]
},
{
"date": "2024-07-21",
"name": "Lagginhorn",
"region": { "country": "Schweiz", "countryCode": "CH", "state": "Wallis", "stateCode": "CH-VS", "group": "Saastal" },
"collection": "Hochtourenwoche 2024",
"segments": [
{
"sport": "alpine",
"peaks": [{ "name": "Lagginhorn", "outcome": "turned_back" }],
"routes": [{ "name": "West ridge", "peak": "Lagginhorn", "grades": { "alpine": "WS+", "uiaa": "III" } }]
}
]
},
{
"date": "2024-09-14",
"name": "Wendenstöcke",
"segments": [
{ "sport": "hiking", "name": "Approach", "gainM": 450, "duration": "1:15" },
{
"sport": "climbing",
"routes": [
{
"name": "Motörhead",
"area": "Gadmen",
"sector": "Wendenstöcke",
"style": "redpoint",
"role": "lead",
"pitches": [
{ "grades": { "french": "6a" }, "lengthM": 35 },
{ "grades": { "french": "6b+" }, "lengthM": 30, "role": "follow" },
{ "grades": { "french": "6a+" }, "lengthM": 40 }
]
},
{ "name": "Warm-up", "area": "Gadmen", "sector": "Wendenstöcke", "grades": { "uiaa": "V+" }, "style": "onsight" }
]
}
]
},
{
"date": "2024-10-05",
"name": "Chironico",
"partners": ["Carla"],
"segments": [
{
"sport": "bouldering",
"routes": [
{ "name": "Dreamtime", "sector": "Chironico Sinistra", "boulder": "Dreamtime block", "grades": { "font": "8B+" }, "style": "attempt", "attempts": 12 },
{ "name": "Ganesh", "sector": "Chironico Sinistra", "boulder": "Ganesh block", "grades": { "font": "7C" }, "style": "flash" }
]
}
]
},
{
"date": "2025-02-08",
"name": "Niesen hike & fly",
"segments": [
{ "sport": "snowshoeing", "gainM": 1600, "duration": "4:10", "grade": { "snowshoe": "WT2" }, "peaks": [{ "name": "Niesen", "elevationM": 2362 }] },
{ "sport": "paragliding", "name": "Flight down", "lossM": 1650, "duration": "0:25" }
]
}
]
}
Capturing a sector topo
A climbing guide, a topo page (theCrag, a club's website), a PDF or a photo of the board at the crag lists a sector's routes: a number, a name, a grade, a length, a line of text. An AI given this page and that listing writes a file of topos — the area, the sector and its routes, with no tour — and the app adds them to the logbook as places and routes you have not climbed yet. Your later tours find them by name.
There are two ways in:
- More › Import data takes a file of
topos(alone or besidetours). Every topo names itsareaand itssector. - On a sector's screen, ⋮ › Import routes (JSON) takes the routes into *that* sector. The file may leave out area and sector — a topo with only
routes, or simply a top-levelrouteslist (second example below); if it names a sector or area, it must be this one (case and spacing ignored), or the file is refused rather than filed elsewhere. On an area's screen the same action takes topos that name theirsector: a sector of that name inside the area is used, else created with its discipline.
Existing routes are never overwritten. A route that the sector already holds — same sport, same name (case and spacing ignored) — keeps its grades, length, pitches and character exactly as they are, and is reported as already present; only a topo number or note it does not have yet is added. Only new routes are created. Importing the same file again therefore adds nothing. A name spelled differently from the logbook's ("Bazilus" beside "Bazillus") becomes a second route: join the two afterwards with the route merge — on the sector's screen, the route's ⋯ › Merge with another route…, or Settings › Tools › Data quality › Duplicate routes, which finds such pairs. The visits move to the route you keep, and its topo takes what it lacked.
One topo is one transaction: a sector comes in whole or not at all, and the report lists per sector whether area and sector were new, how many routes were created and how many were already there.
Mapping a topo listing:
| On the topo | Field |
|---|---|
The number in front of the route (0, 1, 12) | topoIndex |
| The route's name | name |
The grade (7b, 6a+, VI+, 7A) | grades with its scale: French 7b → { "french": "7b" }, UIAA VI+ → { "uiaa": "VI+" }, a boulder grade 7A → { "font": "7A" } |
The length (15m, 25 m) | lengthM as a number: 15 |
Pitches (3 SL, a grade per pitch) | pitches, one entry per pitch with its grade and length; with only the route's grade (5c, 2 SL), the grade in the route's grades and one empty entry {} per pitch |
| "Sport", "bolted" | nothing — sport climbing on bolts is the default (character plaisir); "trad" → trad, "partly bolted" → partly_bolted |
| A description, "Extension of …", "Erstbegehung: …", the first ascent | note, as written |
| Stars, a quality rating, ascent counts, a photo | nothing — not stored; leave them out |
Write each name exactly as the topo spells it, and keep the topo's own grade scale. Leave out what the listing does not say; never fill a grade or a length from elsewhere.
A topo for the data import:
{
"format": "tourbook-import",
"version": 1,
"topos": [
{
"area": { "name": "Balmfluh", "region": { "country": "Schweiz", "countryCode": "CH", "state": "Solothurn", "stateCode": "CH-SO" } },
"sector": { "name": "Hauptwand", "discipline": "rope" },
"routes": [
{ "topoIndex": 1, "name": "Bazillus", "grades": { "french": "7b" }, "lengthM": 12 },
{ "topoIndex": 2, "name": "3 Tiger & 1 Baby", "grades": { "french": "7c+" }, "lengthM": 15, "note": "Extension of Bazillus." },
{
"topoIndex": 3,
"name": "Pipopa",
"grades": { "french": "8b" },
"note": "rechte Verlängerung zu 'Himmel und Hölle'. Erstbegehung: Stefan Schibli"
},
{
"topoIndex": 4,
"name": "Lange Reise",
"pitches": [
{ "grades": { "french": "6a" }, "lengthM": 30 },
{ "grades": { "french": "6b+" }, "lengthM": 25 }
]
}
]
},
{
"area": { "name": "Balmfluh", "region": { "country": "Schweiz", "countryCode": "CH", "state": "Solothurn", "stateCode": "CH-SO" } },
"sector": { "name": "Blockfeld", "discipline": "boulder" },
"boulders": [
{ "name": "Ei", "routes": [{ "topoIndex": 1, "name": "Dotter", "grades": { "font": "6A" } }] }
]
}
]
}
The same routes on the screen of sector "Hauptwand" — no area, no sector:
{
"format": "tourbook-import",
"version": 1,
"routes": [
{ "topoIndex": 1, "name": "Bazillus", "grades": { "french": "7b" }, "lengthM": 12 },
{ "topoIndex": 5, "name": "Vitalstoffe", "grades": { "french": "7a" }, "lengthM": 15 }
]
}
Capturing destinations
A list of summits, huts or crags — a peak-bagging list, a guide's table of
contents, the places of an old logbook — becomes a file of locations:
each with its kind, name and what the list says of it. The app adds the
ones it does not know and leaves the known ones as they are; your later tours
find them by name.
- More › Import data takes a file of
locations, alone or besidetoursandtopos. The destinations are imported first, so the file's tours and topos find them. - A known destination is that destination: matched like a tour's peak or sector (see Finding known places) — by kind and name in the entry's region, a position telling namesakes apart — and never doubled. It only gains a position or height it does not have yet; its name, region and discipline stay its own. A peak or place the logbook lacks but an installed region pack knows comes from the pack, with the pack's position and height.
- A sector with an
arealies inside that area (found by name in the region, else created) and is found by name inside it; a sector of the other discipline there refuses its entry. - One entry is one transaction; the report says how many were created, how many were already there and how many gained a position or height. Importing the same file again adds nothing.
{
"format": "tourbook-import",
"version": 1,
"locations": [
{ "kind": "peak", "name": "Weissmies", "lat": 46.1278, "lon": 8.0118, "elevationM": 4017, "region": { "country": "Schweiz", "countryCode": "CH", "state": "Wallis", "stateCode": "CH-VS", "group": "Saastal" } },
{ "kind": "place", "name": "Almageller Hütte", "elevationM": 2894, "region": { "country": "Schweiz", "countryCode": "CH", "state": "Wallis", "stateCode": "CH-VS" } },
{ "kind": "area", "name": "Balmfluh", "region": { "country": "Schweiz", "countryCode": "CH", "state": "Solothurn", "stateCode": "CH-SO" } },
{ "kind": "sector", "name": "Hauptwand", "area": "Balmfluh", "region": { "country": "Schweiz", "countryCode": "CH", "state": "Solothurn", "stateCode": "CH-SO" } },
{ "kind": "sector", "name": "Blockfeld", "area": "Balmfluh", "discipline": "boulder", "region": { "country": "Schweiz", "countryCode": "CH", "state": "Solothurn", "stateCode": "CH-SO" } }
]
}
Capturing challenge lists
A list to tick off — the summits of a canton, the huts of a guidebook, the
passes of a road trip — becomes a file of challengeLists: each with its
key, name, what its entries are, and the entries. The app shows it on the
Statistics › Challenges tab like the lists it comes with: how many are
done, which tour reached each, and where they are on the map.
- More › Import data takes a file of
challengeLists, alone or beside the other lists. The challenges tab links there. - An entry is done when a tour reaches it: a location of the same name (or one of its
aliases) on a tour — a peak on a list of peaks, a place on a list of huts or passes — reached, not turned back from. The tours already recorded tick the list at once. - Namesakes are told apart by position. Where a name is shared — by two entries of the list, or by two of your locations — and both the entry and the location have a position, the location ticks the entry only within 1000 m of it, and of several such entries the nearest. A name nobody else carries ticks as before, however far off its location is placed. An entry that stands for several regions at one summit repeats its name and its position with each
regionCode, and one visit ticks them all. - An entry with a position is filed under the installed packs where a pack holds a summit, hut or pass of its name within 1000 m: a location picked from the map then ticks it whatever name it carries.
- A list is replaced, never doubled: a list with the key of one of your lists replaces it whole, and the ticks stay — they come from the tours. A key of a list that comes with the app is refused; give yours a key of its own.
- One list is one transaction; a list with any problem is left out whole.
{
"format": "tourbook-import",
"version": 1,
"challengeLists": [
{
"key": "glarner-gipfel",
"name": "Glarner Gipfel",
"targetKind": "peak",
"source": "My own list",
"targets": [
{ "name": "Tödi", "lat": 46.8115, "lon": 8.9146, "elevationM": 3614, "regionName": "Glarus", "regionCode": "CH-GL", "aliases": ["Piz Russein"] },
{ "name": "Glärnisch", "elevationM": 2915, "regionName": "Glarus", "regionCode": "CH-GL" },
{ "name": "Hausstock", "elevationM": 3158 }
]
}
]
}
JSON Schema
The shape of the file as a JSON Schema (draft 2020-12). It checks the structure and the vocabularies; which fields each sport records, and which tokens a scale has, the app checks as described above.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "TourBook import file, version 1",
"description": "The shape of a *.tourbook.json file. Which fields a sport records, and which grade tokens a scale accepts, is checked by the app and described in the guide.",
"type": "object",
"properties": {
"format": { "const": "tourbook-import" },
"version": { "const": 1 },
"tours": { "type": ["array", "null"], "items": { "$ref": "#/$defs/tour" } },
"topos": { "type": ["array", "null"], "items": { "$ref": "#/$defs/topo" } },
"routes": { "type": ["array", "null"], "items": { "$ref": "#/$defs/topoRoute" } },
"locations": { "type": ["array", "null"], "items": { "$ref": "#/$defs/location" } },
"challengeLists": { "type": ["array", "null"], "items": { "$ref": "#/$defs/challengeList" } },
"$schema": { "type": "string" }
},
"required": ["format", "version"],
"anyOf": [
{ "required": ["tours"], "properties": { "tours": { "type": "array" } } },
{ "required": ["topos"], "properties": { "topos": { "type": "array" } } },
{ "required": ["routes"], "properties": { "routes": { "type": "array" } } },
{ "required": ["locations"], "properties": { "locations": { "type": "array" } } },
{ "required": ["challengeLists"], "properties": { "challengeLists": { "type": "array" } } }
],
"additionalProperties": false,
"$defs": {
"text": { "type": "string", "pattern": "\\S" },
"optionalText": { "type": ["string", "null"] },
"elevation": { "type": ["number", "null"], "minimum": -500, "maximum": 9000 },
"pitchLength": { "type": ["number", "null"], "minimum": 0, "maximum": 500 },
"names": { "type": ["array", "null"], "items": { "$ref": "#/$defs/text" } },
"grades": {
"description": "A grade per scale: { \"french\": \"6a\" }. One scale per family (french, uiaa, yds and norwegian are one family).",
"type": ["object", "null"],
"propertyNames": {
"enum": ["french", "uiaa", "yds", "norwegian", "font", "vscale", "ice", "alpine", "mixed", "hiking", "mtb", "via_ferrata", "snowshoe"]
},
"additionalProperties": { "type": "string" }
},
"tour": {
"type": "object",
"properties": {
"date": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"name": { "$ref": "#/$defs/text" },
"description": { "$ref": "#/$defs/optionalText" },
"region": { "$ref": "#/$defs/region" },
"collection": { "$ref": "#/$defs/optionalText" },
"partners": { "$ref": "#/$defs/names" },
"tags": { "$ref": "#/$defs/names" },
"segments": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/segment" } }
},
"required": ["date", "name", "segments"],
"additionalProperties": false
},
"region": {
"type": ["object", "null"],
"properties": {
"country": { "$ref": "#/$defs/optionalText" },
"countryCode": { "type": ["string", "null"], "pattern": "^[A-Z]{2}$" },
"state": { "$ref": "#/$defs/optionalText" },
"stateCode": { "type": ["string", "null"], "pattern": "^[A-Z]{2}-[A-Z0-9]{1,3}$" },
"group": { "$ref": "#/$defs/optionalText" }
},
"minProperties": 1,
"additionalProperties": false
},
"segment": {
"type": "object",
"properties": {
"sport": {
"enum": [
"hiking",
"scrambling",
"alpine",
"climbing",
"ski_touring",
"xc_skiing",
"running",
"orienteering",
"mtb",
"cycling",
"trail_running",
"ice_climbing",
"mixed_climbing",
"paragliding",
"bouldering",
"ski_mountaineering",
"via_ferrata",
"snowshoeing"
]
},
"name": { "$ref": "#/$defs/optionalText" },
"distanceKm": { "type": ["number", "null"], "minimum": 0, "maximum": 1000 },
"gainM": { "type": ["number", "null"], "minimum": 0, "maximum": 15000 },
"lossM": { "type": ["number", "null"], "minimum": 0, "maximum": 15000 },
"duration": { "type": ["string", "null"], "pattern": "^\\d+:[0-5]\\d(:[0-5]\\d)?$" },
"grade": { "$ref": "#/$defs/grades" },
"peaks": { "type": ["array", "null"], "items": { "$ref": "#/$defs/peak" } },
"places": { "type": ["array", "null"], "items": { "$ref": "#/$defs/place" } },
"routes": { "type": ["array", "null"], "items": { "$ref": "#/$defs/route" } }
},
"required": ["sport"],
"additionalProperties": false
},
"peak": {
"type": "object",
"properties": {
"name": { "$ref": "#/$defs/text" },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" },
"outcome": { "enum": ["reached", "turned_back", null] }
},
"required": ["name"],
"additionalProperties": false
},
"place": {
"type": "object",
"properties": {
"name": { "$ref": "#/$defs/text" },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" }
},
"required": ["name"],
"additionalProperties": false
},
"route": {
"type": "object",
"properties": {
"name": { "$ref": "#/$defs/text" },
"peak": { "$ref": "#/$defs/optionalText" },
"place": { "$ref": "#/$defs/optionalText" },
"area": { "$ref": "#/$defs/optionalText" },
"sector": { "$ref": "#/$defs/optionalText" },
"boulder": { "$ref": "#/$defs/optionalText" },
"grades": { "$ref": "#/$defs/grades" },
"pitches": { "type": ["array", "null"], "items": { "$ref": "#/$defs/pitch" } },
"style": { "enum": ["onsight", "flash", "redpoint", "repeat", "french_free", "aid", "attempt", null] },
"role": { "enum": ["lead", "follow", "alternating", null] },
"attempts": { "type": ["integer", "null"], "minimum": 1, "maximum": 999 },
"outcome": { "enum": ["reached", "turned_back", null] }
},
"required": ["name"],
"additionalProperties": false
},
"pitch": {
"type": "object",
"properties": {
"grades": { "$ref": "#/$defs/grades" },
"lengthM": { "$ref": "#/$defs/pitchLength" },
"role": { "enum": ["lead", "follow", "alternating", null] },
"style": { "enum": ["onsight", "flash", "redpoint", "repeat", "french_free", "aid", "attempt", null] }
},
"additionalProperties": false
},
"topo": {
"type": "object",
"properties": {
"area": { "$ref": "#/$defs/topoArea" },
"sector": { "$ref": "#/$defs/topoSector" },
"routes": { "type": ["array", "null"], "items": { "$ref": "#/$defs/topoRoute" } },
"boulders": { "type": ["array", "null"], "items": { "$ref": "#/$defs/topoBoulder" } }
},
"additionalProperties": false
},
"topoArea": {
"type": ["object", "null"],
"properties": {
"name": { "$ref": "#/$defs/text" },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" },
"region": { "$ref": "#/$defs/region" }
},
"required": ["name"],
"additionalProperties": false
},
"topoSector": {
"type": ["object", "null"],
"properties": {
"name": { "$ref": "#/$defs/text" },
"discipline": { "enum": ["rope", "boulder", null] },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" }
},
"required": ["name"],
"additionalProperties": false
},
"topoBoulder": {
"type": "object",
"properties": {
"name": { "$ref": "#/$defs/text" },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" },
"routes": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/topoRoute" } }
},
"required": ["name", "routes"],
"additionalProperties": false
},
"topoRoute": {
"type": "object",
"properties": {
"topoIndex": { "type": ["integer", "null"], "minimum": 0, "maximum": 9999 },
"name": { "$ref": "#/$defs/text" },
"sport": { "enum": ["climbing", "ice_climbing", "mixed_climbing", "bouldering", null] },
"grades": { "$ref": "#/$defs/grades" },
"lengthM": { "type": ["number", "null"], "minimum": 0, "maximum": 3000 },
"pitches": { "type": ["array", "null"], "items": { "$ref": "#/$defs/topoPitch" } },
"character": { "enum": ["plaisir", "partly_bolted", "trad", "alpine", null] },
"note": { "$ref": "#/$defs/optionalText" }
},
"required": ["name"],
"additionalProperties": false
},
"topoPitch": {
"type": "object",
"properties": {
"grades": { "$ref": "#/$defs/grades" },
"lengthM": { "$ref": "#/$defs/pitchLength" }
},
"additionalProperties": false
},
"location": {
"description": "A destination without a tour. `area` and `discipline` are given on a sector only.",
"type": "object",
"properties": {
"kind": { "enum": ["peak", "place", "area", "sector"] },
"name": { "$ref": "#/$defs/text" },
"area": { "$ref": "#/$defs/optionalText" },
"discipline": { "enum": ["rope", "boulder", null] },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" },
"region": { "$ref": "#/$defs/region" }
},
"required": ["kind", "name"],
"additionalProperties": false
},
"challengeList": {
"description": "A challenge list of your own: summits, huts or passes to tick off. A list with the key of one of your lists replaces it.",
"type": "object",
"properties": {
"key": { "type": "string", "pattern": "^[a-z0-9-]+$" },
"name": { "$ref": "#/$defs/text" },
"targetKind": { "enum": ["peak", "hut", "pass"] },
"source": { "$ref": "#/$defs/optionalText" },
"version": { "$ref": "#/$defs/optionalText" },
"targets": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/challengeTarget" } }
},
"required": ["key", "name", "targetKind", "targets"],
"additionalProperties": false
},
"challengeTarget": {
"description": "One entry of a challenge list. A name and region code appear once per list, unless every entry of that name and code has lat and lon and they lie more than 1000 m apart.",
"type": "object",
"properties": {
"name": { "$ref": "#/$defs/text" },
"lat": { "type": ["number", "null"], "minimum": -90, "maximum": 90 },
"lon": { "type": ["number", "null"], "minimum": -180, "maximum": 180 },
"elevationM": { "$ref": "#/$defs/elevation" },
"regionName": { "$ref": "#/$defs/optionalText" },
"regionCode": { "type": ["string", "null"], "pattern": "^[A-Z]{2}(-[A-Z0-9]{1,3})?$" },
"aliases": { "$ref": "#/$defs/names" }
},
"required": ["name"],
"additionalProperties": false
}
}
}