Otripy journey file format
A journey (a trip) is saved as a UTF-8 JSON file, usually with the .json extension. This describes format version 1.2.0, written by Otripy 1.5.0 and later, and the 1.1.0 and 1.0.0 formats it extends.
Otripy writes the oldest format that can hold a journey: 1.2.0 for journeys with routes between locations, 1.1.0 for journeys with groups or trip notes, 1.0.0 otherwise. Format 1.2.0 adds routes at the top level (#50). Format 1.1.0 adds:
notesat the top level: the trip's general notes (#19);groupsat the top level, andgroupin locations: titled sections of the location list (#18).
Top level
{
"format": "otripy",
"description": "A Journey with Otripy",
"format_version": "1.0.0",
"app_version": "1.2.3",
"app_name": "Otripy",
"created_at": "2025-03-16T14:20:41.146103+00:00",
"updated_at": "2025-03-17T18:43:54.281403+00:00",
"encoding": "UTF-8",
"settings": {},
"locations": [ ... ]
}
| Key | Type | Meaning |
|---|---|---|
format |
string | Always "otripy". Files without it are not journeys (see legacy format). |
description |
string | Free text, currently always "A Journey with Otripy". |
format_version |
string | Version of this file format, MAJOR.MINOR.PATCH. |
app_version |
string | Version of the Otripy that saved the file. |
app_name |
string | Always "Otripy". |
created_at |
string | When the journey was first saved, ISO 8601 with time zone. Kept on later saves. |
updated_at |
string | When the journey was last saved, ISO 8601 with time zone. |
encoding |
string | Always "UTF-8". |
settings |
object | Reserved for journey settings, currently empty. |
locations |
array | The locations, in the order shown in the list: ungrouped locations first, then those of each group, in group order. |
notes |
object | Format 1.1.0. The trip's general notes, a note like those of locations. |
groups |
array | Format 1.1.0. The groups, in the order shown in the list. |
routes |
array | Format 1.2.0. The routes between two locations. |
Locations
{
"id": "0b1e6a52-6f1c-4c55-9a7e-2f0d1c3a4b01",
"lat": 48.8584,
"lon": 2.2945,
"note": {
"markdown": "# Tour Eiffel\n\nChamp de Mars, 5 Avenue Anatole France, 75007 Paris, France\n\n\n\n",
"images": {
"image_5f0c2e8e9a7b4c1d8e2f3a4b5c6d7e8f": "iVBORw0KGgoAAAANSUhEUgAA..."
}
},
"marker": "star",
"color": "purple"
}
| Key | Type | Meaning |
|---|---|---|
id |
string | Unique identifier, a UUID. |
lat, lon |
number | Latitude and longitude, in degrees (WGS 84). |
note |
object | The location's note, see below. |
marker |
string or null |
Name of the Font Awesome icon shown in the marker, without the fa- prefix (e.g. "star", "bed"). null shows a circle. |
group |
string | Optional, format 1.1.0. The id of the location's group. |
color |
string or null |
Marker color, a Leaflet.awesome-markers color. Otripy's color picker offers red, darkred, lightred, orange, green, darkgreen, lightgreen, blue, darkblue, lightblue, cadetblue, purple, pink, white, gray and black. null means blue. |
Groups
Format 1.1.0. A group is a titled section of the location list, such as a day of the trip.
{
"id": "9d2a1c3e-0000-4000-8000-000000000001",
"note": {"markdown": "# Day 1: Left bank\n\nMostly on foot.\n\n", "images": {}},
"collapsed": false
}
| Key | Type | Meaning |
|---|---|---|
id |
string | Unique identifier, a UUID. |
note |
object | The group's note; its first line is the group's title. |
collapsed |
boolean | Whether the group's locations are hidden in the list. |
A location belongs to a group through its optional group key, the group's id; locations without it are not in any group.
Routes
Format 1.2.0. A route between two locations, computed for one travel mode, as drawn on the map.
{
"id": "4c1f0e1a-0000-4000-8000-000000000001",
"from": "0b1e6a52-6f1c-4c55-9a7e-2f0d1c3a4b01",
"to": "0b1e6a52-6f1c-4c55-9a7e-2f0d1c3a4b02",
"mode": "foot",
"distance": 3748.2,
"duration": 3002.1,
"geometry": [[48.8584, 2.2945], [48.8592, 2.3011], [48.8606, 2.3376]]
}
| Key | Type | Meaning |
|---|---|---|
id |
string | Unique identifier, a UUID. |
from, to |
string | The id of the locations the route goes from and to. |
mode |
string | car, bike or foot. |
distance |
number | Length, in meters. |
duration |
number | Duration, in seconds, as estimated by the routing service. |
geometry |
array | The path, as [latitude, longitude] pairs. |
There is at most one route between two locations. Routes whose from or to matches no location are dropped when reading.
Notes
| Key | Type | Meaning |
|---|---|---|
markdown |
string | The note's text, in Markdown as written by Qt (QTextDocument::toMarkdown). Its first line, without heading marks, is the location's title. |
images |
object | The images the note shows, by name: base64-encoded PNG data. |
image_widths |
object | Optional. The display width, in pixels, of resized images, by name. Images not listed are shown at their original size. Written by Otripy 1.3 and later. |
An image appears in the text as , where name is a key of images. Otripy saves exactly the images the text references. Names are image_ followed by a random hexadecimal identifier; files saved by Otripy 1.2.3 and earlier use dropped_image_1, dropped_image_2, and so on.
Reading rules
When opening a file, Otripy:
- refuses files without
"format": "otripy", unless they use the legacy format below; - refuses files whose
format_versionis newer than the one it knows, asking to update Otripy; theapp_versiondoes not matter, since newer versions write older formats when they can; - treats a location whose
groupmatches no group as ungrouped; - accepts missing location fields:
latandlondefault to0,noteto an empty note,idto a new UUID; empty strings formarkerandcolor(written by Otripy 1.1) meannull; - accepts a
notethat is a plain string instead of an object, as its Markdown text.
Legacy format
Before format 1.0.0 (Otripy 1.1 and earlier), a file is a bare JSON array of locations, without the top-level object. Otripy still opens these files and saves them in the current format.
Changing the format
Otripy refuses files whose format_version is newer than the one it knows, and writes the oldest format that can hold a journey (BASE_FORMAT_VERSION or CURRENT_FORMAT_VERSION in src/otripy/journey.py). So:
- A new optional field that older versions can ignore without losing meaning (like
image_widths: they just show images at their original size) keeps the version. - A field older versions cannot ignore, because they would lose or misread data when saving the file again (like groups and trip notes), bumps
CURRENT_FORMAT_VERSION. Journeys that do not use it keep being written in the previous format.
In both cases, update this document and add a file showing the new shape to tests/fixtures/.
Otripy 1.3.1 and earlier also refuse any file saved by a newer Otripy version, whatever its format: they check app_version too. Only versions from 1.4.0 on follow the rules above.