komoog¶
About¶
Convert your komoot hiking/cycling trips to audio signals.
repository: https://github.com/benmaier/komoog/
documentation: http://komoog.readthedocs.io/
from komoog.komoot import download_all_komoot_tours, choose_downloaded_komoot_tour
from komoog.audio import convert_tour_to_audio, play_audio
download_all_komoot_tours()
tour = choose_downloaded_komoot_tour()
audio, sampling_rate = convert_tour_to_audio(tour,
approximate_length_in_seconds=4,
set_tune_to_follow_tour_profile=True,
)
play_audio(audio, sampling_rate)
After hiking I noticed that komoot comes with elevation profiles of tour hiking trips:

This reminded me of wave tables I know from sound synthesis. Because I'm always looking for sounds to use when making music, I decided to write code that generates sounds from hiking profiles that can be used in sound synthesis.
Note that I adapted code from js-on/medium_komoot to access trips on komoot.
Examples¶
Colorado Provencale in Rustrel - La Doa Loop from Rustrel¶
Lookout - L'Aiguebrun Loop from Buoux¶
Gorges de Régalon - Vue de la Gorge Loop from Quartier Gardet¶
Forêt des Cèdres - Vue au sud - Belvédère Loop from Lacoste¶
Crête du Grand luberon - Le Mourre Nègre (1125m) Loop from Rue de l'Église¶
Install¶
pip install komoog
komoog
was developed and tested for
Python 3.6
Python 3.7
Python 3.8
So far, the package's functionality was tested on macOS only.
Prerequisites¶
Save your komoot credentials in ~/.komoog/komoot.json
as
{
"email" : "your@email.com",
"password" : "yourpassword",
"clientid" : "yourclientid"
}
You can find your client id in the komoot url when you log in. Click on your username, then on "Planned Tours" or "Completed Tours". The URL will change to something like
https://www.komoot.com/user/1851102841208/tours?type=planned
Here, 1851102841208
is your clientid
.
Dependencies¶
komoog
directly depends on the following packages which will be
installed by pip
during the installation process
numpy>=1.17
scipy>=1.5.0
gpxpy>=1.4.2
simplejson>=3.17.2
simpleaudio=>=1.0.4
matplotlib>=3.0.0
Documentation¶
The full documentation is available at komoog.readthedocs.io.
Changelog¶
Changes are logged in a separate file.
License¶
This project is licensed under the MIT License. Note that this excludes any images/pictures/figures shown here or in the documentation.
Contributing¶
If you want to contribute to this project, please make sure to read the code of conduct and the contributing guidelines. In case you're wondering about what to contribute, we're always collecting ideas of what we want to implement next in the outlook notes.
Dev notes¶
Fork this repository, clone it, and install it in dev mode.
git clone git@github.com:YOURUSERNAME/komoog.git
make
If you want to upload to PyPI, first convert the new README.md
to
README.rst
make readme
It will give you warnings about bad .rst
-syntax. Fix those errors in
README.rst
. Then wrap the whole thing
make pypi
It will probably give you more warnings about .rst
-syntax. Fix those
until the warnings disappear. Then do
make upload
Tutorial¶
import komoog.komoot as komoot
import komoog.audio as audio
import matplotlib.pyplot as pl
Download komoot tours¶
tours = komoot.download_all_komoot_tours()
for i, tour in enumerate(tours):
print(i, tour['name'])
0 Colorado Provencale in Rustrel – La Doa Loop from Rustrel
1 Lookout – L´Aiguebrun Loop from Buoux
2 Gorges de Régalon – Vue de la Gorge Loop from Quartier Gardet
3 Forêt des Cèdres - Vue au sud – Belvédère Loop from Lacoste
4 Crête du Grand luberon – Le Mourre Nègre (1125m) Loop from Rue de l'Église
5 Beautiful Cliffs – Gorges d'Oppedette Loop from D 201
6 Beautiful Cliffs – Gorges d'Oppedette Loop from D 201
7 Chateau des Eveques Loop from Fontaine-de-Vaucluse
8 Valescure vaucluse Loop from Fontaine-de-Vaucluse
9 Colorado Provencale in Rustrel – Aussicht auf die Sahara Loop from Rustrel
10 Chateau des Eveques – Belle vue Loop from Fontaine-de-Vaucluse
11 Porte de Saignon – Ortskern Saignon Loop from Apt
12 Ortskern Saignon – Porte de Saignon Loop from Apt
13 Möllensee und Kiessee
Load komoot tours from harddrive¶
Tours are saved in ~/.komoog/
.
import komoog.io as io
tours = io.read_tours()
Plot elevation profile¶
import komoog.gpx as gpx
gpx_tracks = gpx.convert_tour_to_gpx_tracks(tours[2])
distance, elevation = gpx.convert_gpx_tracks_to_arrays(gpx_tracks)
pl.plot(distance, elevation)
pl.xlabel('distance [m]')
pl.ylabel('elevation [m]')

Convert elevation profile to signal¶
This will normalize the profile to the correct ranges and remove duplicates
x, y = audio.convert_distance_and_elevation_to_signal(distance, elevation)
pl.plot(x,y )

By default, signals will be maximized. If you don’t want them maximized, define a maximum elevation difference that will correspond to the range [-1,1] instead, e.g. 2000 meters.
x, y = audio.convert_distance_and_elevation_to_signal(distance,
elevation,
max_elevation_difference=2000)
pl.plot(x,y)
pl.ylim(-1,1)

Convert signal to audio¶
# approximate_length_in_seconds = 0 will give a single loop of the signal
audio_data, sampling_rate = audio.convert_signal_to_audio(x,
y,
approximate_length_in_seconds=0
)
pl.plot(audio_data)

Loop wave¶
audio_data, sampling_rate = audio.convert_signal_to_audio(
x,
y,
approximate_length_in_seconds=1/100
)
pl.plot(audio_data)

Generate longer audio data and play it¶
audio_data, sampling_rate = audio.convert_signal_to_audio(x,y,)
audio.play_audio(audio_data, sampling_rate)
You should’ve heard a sound now. Note that by default, a sound length of 1s is produced.
Tune to different notes¶
audio_data, sampling_rate = audio.convert_signal_to_audio(
x,
y,
tune='A'
)
audio.play_audio(audio_data, sampling_rate)
These are the tunes that work:
audio._NOTES
{'C': -9,
'C#': -8,
'Db': -8,
'D': -7,
'D#': -6,
'Eb': -6,
'E': -5,
'F': -4,
'F#': -3,
'Gb': -3,
'G': -2,
'G#': -1,
'Ab': -1,
'A': 0,
'A#': 1,
'Bb': 1,
'B': 2}
You can also tune to a specific frequency, e.g. 200 Hz.
audio_data, sampling_rate = audio.convert_signal_to_audio(
x,
y,
tune=220,
)
audio.play_audio(audio_data, sampling_rate)
Change sampling rate¶
audio_data, sampling_rate = audio.convert_signal_to_audio(
x,
y,
sampling_rate=48000,
)
audio.play_audio(audio_data, sampling_rate)
Write .wav file¶
io.write_wav('./example.wav',audio_data,sampling_rate)
Converting tours to sound without going through all the hassle¶
audio_data, sampling_rate = audio.convert_tour_to_audio(
tours[2],
max_elevation_difference=1000,
sampling_rate=48000,
tune='A',
approximate_length_in_seconds=3,
)
audio.play_audio(audio_data, sampling_rate)
Make the frequency of the sound follow the elevation profile¶
audio_data, sampling_rate = audio.convert_tour_to_audio(
tours[2],
max_elevation_difference=1000,
sampling_rate=48000,
tune='A#',
approximate_length_in_seconds=3,
set_tune_to_follow_tour_profile=True,
)
audio.play_audio(audio_data, sampling_rate)
Interactive conversion without downloading all tours previously¶
tour = komoot.choose_komoot_tour_live()
audio_data, sampling_rate = audio.convert_tour_to_audio(tour)
audio.play_audio(audio_data, sampling_rate)
Play some melodies¶
notes = ['C','D','E','F','G','F','E','D','C']
durations = [0.25]* 8 + [0.5] #seconds
import numpy as np
audios = []
for dur, note in zip(durations, notes):
audio_data, sampling_rate = audio.convert_tour_to_audio(
tours[2],
max_elevation_difference=1000,
tune=note,
approximate_length_in_seconds=dur,
)
audios.append(audio_data)
audio.play_audio(np.concatenate(audios).astype(np.int16), sampling_rate)
# with pauses
durations = ([0.25]*6+[0.5])*2 #seconds
notes = ['C','C','G','G','A','A','G','F','F','E','E','D','D','C']
pause = np.zeros((1000,))
audios = []
for dur, note in zip(durations, notes):
audio_data, sampling_rate = audio.convert_tour_to_audio(
tours[2],
max_elevation_difference=1000,
tune=note,
approximate_length_in_seconds=dur,
)
audios.extend([audio_data,pause])
audio.play_audio(np.concatenate(audios).astype(np.int16), sampling_rate)
Komoot¶
Obtaining tours and tour data from komoot. Adapted from https://github.com/js-on/medium_komoot.
- komoog.komoot.choose_downloaded_komoot_tour()[source]¶
Choose a previously downloaded tour. Tour can be passed to
komoog.gpx.convert_tour_to_gpx_tracks()
afterwards.
- komoog.komoot.choose_komoot_tour_live()[source]¶
Login with user credentials, download tour information, choose a tour, and download it. Can be passed to
komoog.gpx.convert_tour_to_gpx_tracks()
afterwards.
- komoog.komoot.download_all_komoot_tours()[source]¶
Login with user credentials, download tour information and all tours. Tours will be saved in a custom directory. Tours can be passed to
komoog.gpx.convert_tour_to_gpx_tracks()
afterwards.
Audio¶
Audio handling and conversion.
- komoog.audio.convert_distance_and_elevation_to_audio(distance, elevation, max_elevation_difference=0, tune='C', sampling_rate=44100, approximate_length_in_seconds=1)[source]¶
Convert a distance/elevation profile to an audio signal.
- Parameters
distance (numpy.ndarray) -- Contains the covered 2D distance in meters.
elevation (numpy.ndarray) -- Contains the corresponding elevation profile in meters
max_elevation_difference (float, default = 0) -- Used to control the level of the audio signal. If this value is
<= 0
, the audio level will always be maximized. If given a positive value, this value will represent the maximum scale of the audio signal. If the elevation profile's elevation difference is larger than this value, the signal will simply be maximized. A good value ismax_elevation_difference = 2000
.Desired frequency of the sound. Can be any of
[ 'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'Bb', 'B' ]
where
'A'
corresponds to 440Hz.Can also be a frequency in Hz.
sampling_rate (int, default = 44100) -- Sampling rate in Hz
approximate_length_in_seconds (float, default = 1.) -- The desired length of the audio signal in seconds If equal to zero, will return a single loop.
- Returns
audio (numpy.ndarray of numpy.int16) -- The transformed audio signal
sampling_rate (int) -- The sampling rate of the audio signal.
- komoog.audio.convert_distance_and_elevation_to_profile_audio(distance, elevation, max_elevation_difference=0, tune='C', sampling_rate=44100, approximate_length_in_seconds=1)[source]¶
Convert a distance/elevation profile to an audio signal that mimicks the elevation profile.
- Parameters
distance (numpy.ndarray) -- Contains the covered 2D distance in meters.
elevation (numpy.ndarray) -- Contains the corresponding elevation profile in meters
max_elevation_difference (float, default = 0) -- Used to control the level of the audio signal. If this value is
<= 0
, the audio level will always be maximized. If given a positive value, this value will represent the maximum scale of the audio signal. If the elevation profile's elevation difference is larger than this value, the signal will simply be maximized. A good value ismax_elevation_difference = 2000
.Desired frequency of the sound. Can be any of
[ 'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'Bb', 'B' ]
where
'A'
corresponds to 440Hz.Can also be a frequency in Hz.
sampling_rate (int, default = 44100) -- Sampling rate in Hz
approximate_length_in_seconds (float, default = 1.) -- The desired length of the audio signal in seconds If equal to zero, will return a single loop.
- Returns
audio (numpy.ndarray of numpy.int16) -- The transformed audio signal
sampling_rate (int) -- The sampling rate of the audio signal.
- komoog.audio.convert_distance_and_elevation_to_signal(distance, elevation, max_elevation_difference=0)[source]¶
Convert a distance/elevation profile to a signal, i.e. normalize distance to range [0,1] and y to range [-1,1].
- Parameters
distance (numpy.ndarray) -- Contains the covered 2D distance in meters.
elevation (numpy.ndarray) -- Contains the corresponding elevation profile in meters
max_elevation_difference (float, default = 0) -- Used to control the level of the audio signal. If this value is
<= 0
, the audio level will always be maximized. If given a positive value, this value will represent the maximum scale of the audio signal. If the elevation profile's elevation difference is larger than this value, the signal will simply be maximized. A good value ismax_elevation_difference = 2000
.
- Returns
x (numpy.ndarray) -- covered distance in range [0,1]
y (numpy.ndarray) -- signal in range [-1,1]
- komoog.audio.convert_signal_to_audio(x, y, tune='C', sampling_rate=44100, approximate_length_in_seconds=1)[source]¶
Convert a normalized distance/elevation signal to an audio signal.
- Parameters
distance (numpy.ndarray) -- Contains the covered 2D distance in meters.
elevation (numpy.ndarray) -- Contains the corresponding elevation profile in meters
Desired frequency of the sound. Can be any of
[ 'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'Bb', 'B' ]
where
'A'
corresponds to 440Hz.Can also be a frequency in Hz.
sampling_rate (int, default = 44100) -- Sampling rate in Hz
approximate_length_in_seconds (float, default = 1.) -- The desired length of the audio signal in seconds If equal to zero, will return a single loop.
- Returns
audio (numpy.ndarray of numpy.int16) -- The transformed audio signal
sampling_rate (int) -- The sampling rate of the audio signal.
- komoog.audio.convert_tour_to_audio(tour, max_elevation_difference=0, tune='C', sampling_rate=44100, approximate_length_in_seconds=1, set_tune_to_follow_tour_profile=False)[source]¶
Convert a hiking tour to audio.
- Parameters
tour (dict) -- A komoot tour item as provided by e.g.
komoog.io.read_tours()
.max_elevation_difference (float, default = 0) -- Used to control the level of the audio signal. If this value is
<= 0
, the audio level will always be maximized. If given a positive value, this value will represent the maximum scale of the audio signal. If the elevation profile's elevation difference is larger than this value, the signal will simply be maximized. A good value ismax_elevation_difference = 2000
.Desired frequency of the sound. Can be any of
[ 'C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'Bb', 'B' ]
where
'A'
corresponds to 440Hz.Can also be a frequency in Hz.
sampling_rate (int, default = 44100) -- Sampling rate in Hz
approximate_length_in_seconds (float, default = 1.) -- The desired length of the audio signal in seconds If equal to zero, will return a single loop.
set_tune_to_follow_tour_profile (bool, defaukt = False) -- If set to
True
the tune of the returned audio signal will follow the tour profile.
- Returns
audio (numpy.ndarray of numpy.int16) -- The transformed audio signal
sampling_rate (int) -- The sampling rate of the audio signal.
GPX¶
GPX file handling.
- komoog.gpx.convert_gpx_tracks_to_arrays(gpx_tracks)[source]¶
Take a list of gpx Track objects and convert them to two arrays, one containing the two-dimensional distance covered on the globe, the second containing the elevation.
- Parameters
gpx_tracks (list of gpx.Track) -- list of tracks to convert
- Returns
distance (numpy.ndarray) -- Contains the covered 2D distance in meters.
elevation (numpy.ndarray) -- Contains the corresponding elevation profile in meters
I/O¶
File I/O.
- komoog.io.read_gpx(fn)[source]¶
Read a gpx file. Returns a gpxpy.GPX object. Pass to
komoog.gpx.convert_gpx_tracks_to_arrays()
asgpx = read_gpx('Tour.gpx') convert_gpx_tracks_to_arrays(gpx.tracks)
to retrieve distance and elevation profile.
Plot¶
Plotting signals.
- komoog.plot.plot_signal(x, y, gradientphases=5, alpha=0.2, figsize=(4, 1))[source]¶
Plot a signal
- Parameters
- Returns
ax -- The axis on which the signal was plotted
- Return type
matplotlib.Axes