Editar o consultar tracks en python - Añadir Waypoints
Todos los track que utilizo los creo, preparo o modifico con QMapShack, aplicación de código libre con la que podemos planificar rutas o visualizar y archivar todos los registros que creamos con el GPS. En algunos casos puede ser interesante modificar los track directamente para realizar tareas a muchos track o modificaciones que sigan algún tipo de patrón. En este caso lo que necesitaba es generar puntos (waypoints) en un track cada ciertos metros, y eso es exactamente lo que vamos a hacer en python.
Para trabajar con ficheros GPX podemos usar una librería en python, concretamente gpxpy, un parser que nos va a facilitar mucho todo el trabajo.
Como ejemplo del uso de esta librería, voy a mostrar una pequeña aplicación en python que hice hace tiempo y que utilizo habitualmente cuando creo los track. Concretamente lo que quiero es generar puntos en un track cada ciertos metros de forma automática. A la aplicación le pasamos el fichero o ficheros GPX y lo que hace es seguir las rutas que tenga el fichero y generar waypoints cada ciertos metros, por defecto 500m, pero podemos seleccionar cada cuanto queremos que nos cree los waypoints.
Lo primero es instalar la librería. Para instalar las librerías vamos a usar pip, voy a utilizar ubuntu 20.04LTS pero es similar en otras distros. Instalamos pip si no lo tenemos todavía en el equipo:
sudo apt install python3-pip
y ahora instalamos la librería:
sudo pip3 install gpxpy
Ahora vamos a ver el código del programa:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ***************************************************************
# *
# * (c) Author: David Quiroga
# * e-mail: david (at) clibre (dot) io
# *
# ****************************************************************
# * Description:
# *
# * Program to read the tracks of a gpx file and create points
# * every nnn meters
# *
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import gpxpy
from pathlib import Path
import sys as mod_sys
import logging as mod_logging
import argparse as mod_argparse
def create_Waypoints(gpx, cdistancia, nfich):
gpx_file = open(nfich, 'w')
wpf_gpx = gpxpy.gpx.GPX()
wpf_gpx.creator = 'ComunidadLibre'
wpf_gpx.name = 'Waypoints (%s)' % gpx.name
wpf_gpx.description = 'Puntos creados cada %d metros' % cdistancia
#wpf_gpx.time = mod_datetime.datetime(2014, 4, 7, 21, 17, 39)
#wpf_gpx.bounds = gpxpy.gpx.GPXBounds(1, 2, 3, 4)
wpf_gpx.author_name = 'david'
#wpf_gpx.author_email = 'david[at]comunidadlibre[dot]org'
wpf_gpx.link = 'comunidadlibre.org'
wpf_gpx.link_text = 'Comunidad Libre - Software Libre'
wpf_gpx.keywords = ''
for track in gpx.tracks:
for segment in track.segments:
previous_point = None
tot_distancia = 0
ttt_distancia = 0
cont=0
for point in segment.points:
if previous_point:
distancia = point.distance_2d(previous_point)
tot_distancia = tot_distancia + distancia
if tot_distancia >= (cdistancia*(cont+1)):
d_waypoint = gpxpy.gpx.GPXWaypoint()
d_waypoint.latitude = point.latitude
d_waypoint.longitude = point.longitude
d_waypoint.elevation = point.elevation
d_waypoint.name = 'Km %0.02f' % (tot_distancia/1000)
d_waypoint.symbol = 'Waypoint'
d_waypoint.comment = 'cm'
d_waypoint.description = 'des'
d_waypoint.dgps_id = str(cont+1)
wpf_gpx.waypoints.append(d_waypoint)
cont=cont+1
else:
# Grabamos el primer punto
d_waypoint = gpxpy.gpx.GPXWaypoint()
d_waypoint.latitude = point.latitude
d_waypoint.longitude = point.longitude
d_waypoint.elevation = point.elevation
d_waypoint.name = 'Km '+ '0.00'
d_waypoint.symbol = 'Waypoint'
d_waypoint.comment = 'cm'
d_waypoint.description = 'des'
d_waypoint.dgps_id = '0'
wpf_gpx.waypoints.append(d_waypoint)
previous_point = point
print (' - Created Waypoints for track [%s]: %d' % (track.name, cont))
gpx_file.write(wpf_gpx.to_xml())
def print_gpx_info(gpx, gpx_file):
print('[+] File: %s' % gpx_file)
if gpx.name:
print(' - GPX name: %s' % gpx.name)
if gpx.description:
print(' - GPX description: %s' % gpx.description)
if gpx.author_name:
print(' - Author: %s' % gpx.author_name)
if gpx.author_email:
print(' - Email: %s' % gpx.author_email)
print(' - Waypoints every: %d meters' % args.distancia)
nfich = replace_last (gpx_file.name, '.gpx', '')
nfich += '-points%dm.gpx'%args.distancia
print(' - File for write Waypoints: %s' % nfich)
existe_file = Path(nfich)
if existe_file.exists() and args.remplazar == 'false':
print(' the file already exists, it will not be modified '
'(to overwrite use: -r true)')
return
else:
create_Waypoints(gpx, args.distancia, nfich)
def replace_last(string, find, replace):
reversed = string[::-1]
replaced = reversed.replace(find[::-1], replace[::-1], 1)
return replaced[::-1]
def run(gpx_files):
if not gpx_files:
print('No GPX file has been selected')
mod_sys.exit(1)
for gpx_file in gpx_files:
try:
gpx = gpxpy.parse(open(gpx_file))
print_gpx_info(gpx, gpx_file)
except Exception as e:
mod_logging.exception(e)
print('Error processing %s' % gpx_file)
mod_sys.exit(1)
def range_type(astr, min=50, max=20000):
value = int(astr)
if min<= value <= max:
return value
else:
raise mod_argparse.ArgumentTypeError('Value out of range: %sm-%sm'%
(min,max))
def check_files(astr):
gpx_file = Path(astr)
if gpx_file.exists():
return gpx_file
else:
raise mod_argparse.ArgumentTypeError('file not found [%s]'
% astr)
def make_parser():
parser = mod_argparse.ArgumentParser(usage='%(prog)s [-d nnn] file1.gpx '
'file2.gpx ..', description='Program to create Waypoints '
'from a track every [nnn] meters')
parser.add_argument('gpxfiles', metavar='files.gpx',
type=check_files, nargs='+', help='gpx files with tracks')
parser.add_argument('-d', '--distance', type=range_type, default=500,
dest='distancia', metavar='nnn',
help='Waypoints distance (from 50m to 2000m), '
'default 500 meters')
parser.add_argument('-r', '--replace', choices={'true', 'false'},
default='false', dest='remplazar', metavar='true/false',
help='replace output files if they already exit, default false')
return parser
if __name__ == '__main__':
args = make_parser().parse_args()
run(args.gpxfiles)
mod_sys.exit(0)
Solo lo tenemos que guardar en nuestro equipo, yo lo he llamado createwaypoints, y darle permisos de ejecución como vimos en la entrada sobre los permisos en GNU/Linux:
sudo chmod +x createwaypoints
Para poder ejecutarlo desde cualquier sitio lo guardamos en /user/local/bin:
sudo cp createwaypoints /usr/local/bin/
y ya podemos usarlo:
$ createwaypoints -h
usage: createwaypoints [-d nnn] file1.gpx file2.gpx ..
Program to create Waypoints from a track every [nnn] meters
positional arguments:
files.gpx gpx files with tracks
optional arguments:
-h, --help show this help message and exit
-d nnn, --distance nnn
Waypoints distance (from 50m to 2000m), default 500 meters
-r true/false, --replace true/false
replace output files if they already exit, default false
david@clibre:~/tracks$ createwaypoints TejoMilenario-Pozas.gpx
[+] File: TejoMilenario-Pozas.gpx
- GPX name: TejoMilenario-Pozas
- Waypoints every: 500 meters
- File for write Waypoints: TejoMilenario-Pozas-points500m.gpx
- Created Waypoints for track [2017-08-23 00:18:52]: 23
david@clibre:~/tracks$ ls
TejoMilenario-Pozas.gpx TejoMilenario-Pozas-points500m.gpx
El programa no modifica el .gpx original sino que crea un nuevo fichero .gpx que deriba del nombre original añadiendo '-pointsXXXm' (donde XXX son los metros).
Por defecto nos crea los waypoints cada 500 metros. Podemos crearlos, por ejemplo cada 100m:
$ createwaypoints TejoMilenario-Pozas.gpx -d 100
[+] File: TejoMilenario-Pozas.gpx
- GPX name: TejoMilenario-Pozas
- Waypoints every: 100 meters
- File for write Waypoints: TejoMilenario-Pozas-points100m.gpx
- Created Waypoints for track [2017-08-23 00:18:52]: 118
Es posible crear los waypoints a varios ficheros, por ejemplo a todos los de una carpeta con extensión gpx:
david@clibre:~/tracks$ ls
ascension-pico-del-oso.gpx LaCabrera-PicoMiel-CanchoGordo.gpx raquetas-picodeloso.gpx
Canon-Rio-Dulce.gpx las-torres-de-la-pedriza.gpx sierra-de-ayllon.gpx
carro-del-diablo.gpx loschorrosdelapedri.gpx silla-del-rey-cascadas-la-chorranca.gpx
el-trabuquete.gpx pozas-del-aljibe.gpx tres-cestos.gpx
david@clibre:~/tracks$ createwaypoints *.gpx
[+] File: ascension-pico-del-oso.gpx
- GPX name: ascension-con-raquetas-al-pico-del-oso-desde-Ortigosa
- Waypoints every: 500 meters
- File for write Waypoints: ascension-pico-del-oso-points500m.gpx
- Created Waypoints for track [Mujer Muerta. Ascensión con raquetas al pico del Oso desde O...]: 21
[+] File: Canon-Rio-Dulce.gpx
- GPX name: Canon Rio Dulce - New
- Waypoints every: 500 meters
- File for write Waypoints: Canon-Rio-Dulce-points500m.gpx
- Created Waypoints for track [Canon del Rio Dulce - New]: 5
- Created Waypoints for track [Canon del Rio Dulce - New]: 6
[+] File: carro-del-diablo.gpx
- Waypoints every: 500 meters
- File for write Waypoints: carro-del-diablo-points500m.gpx
[+] File: el-trabuquete.gpx
- GPX name: el-trabuquete
- Waypoints every: 500 meters
- File for write Waypoints: el-trabuquete-points500m.gpx
- Created Waypoints for track [El Trabuquete]: 28
- Created Waypoints for track [El Trabuquete_rev]: 28
[+] File: LaCabrera-PicoMiel-CanchoGordo.gpx
- GPX name: LaCabrera-PicoMiel-CanchoGordo
- Waypoints every: 500 meters
- File for write Waypoints: LaCabrera-PicoMiel-CanchoGordo-points500m.gpx
- Created Waypoints for track [2016-11-01 16:39:41]: 23
[+] File: las-torres-de-la-pedriza.gpx
- Waypoints every: 500 meters
- File for write Waypoints: las-torres-de-la-pedriza-points500m.gpx
- Created Waypoints for track [Las Torres de la Pedriza Circular por Collado de la Ventana ...]: 27
[+] File: loschorrosdelapedri.gpx
- GPX name: loschorrosdelapedrizaedit2
- Waypoints every: 500 meters
- File for write Waypoints: loschorrosdelapedri-points500m.gpx
- Created Waypoints for track [Los Chorros de la Pedriza]: 19
[+] File: pozas-del-aljibe.gpx
- Waypoints every: 500 meters
- File for write Waypoints: pozas-del-aljibe-points500m.gpx
[+] File: raquetas-picodeloso.gpx
- GPX name: raquetas-picodeloso
- Waypoints every: 500 meters
- File for write Waypoints: raquetas-picodeloso-points500m.gpx
- Created Waypoints for track [Track actual: 12 MAR 2016 10:09 (2 - 624) & other]: 22
[+] File: sierra-de-ayllon.gpx
- Waypoints every: 500 meters
- File for write Waypoints: sierra-de-ayllon-points500m.gpx
- Created Waypoints for track [Sierra de Ayllón, Matallana - Pozas del Aljibe]: 23
[+] File: silla-del-rey-cascadas-la-chorranca.gpx
- GPX name: silla-del-rey-cascadas-la-chorranca-cueva-del-monje
- Waypoints every: 500 meters
- File for write Waypoints: silla-del-rey-cascadas-la-chorranca-points500m.gpx
- Created Waypoints for track [Silla del Rey - Cascadas La Chorranca - Cueva del Monje y Ce...]: 27
[+] File: tres-cestos.gpx
- Waypoints every: 500 meters
- File for write Waypoints: tres-cestos-points500m.gpx
david@clibre:~/tracks$
Nos crea un fichero con los waypoints por cada fichero que le pasemos. Dentro del fichero crea los waypoints de todos los track o secciones que hay. Si no hay ninguno crea el fichero .gpx sin datos.
Los ficheros .gpx con los waypoints los podemos usar en QMapShack o cargarlo al GPS. Espero que sea de utilidad!