Adding elevation to a .gpx file
.gpx files often come without elevation data, or they use a low resolution dataset that’s small enough to pre-load onto a device like a sports watch. With a bit of Python code and the GPXZ API, it’s easy to add high-resolution elevation data to your activity files.
First, use gpxpy to read the gpx file.
import gpxpy
# Load gpx.
gpx_path = 'run.gpx'
with open(gpx_path) as f:
gpx = gpxpy.parse(f)
Next, here’s a fuction that takes latitude and longitude coordinates, and returns hi-resolution elevation data from the GPXZ API
import numpy as np
import requests
API_KEY = 'ak_demo_1234' # Get a free key from www.gpxz.io
BATCH_SIZE = 50 # 512 for paid customers.
def gpxz_elevation(lats, lons):
'''Iterate over the coordinates in chunks, querying the GPXZ api to return
a list of elevations in the same order.'''
elevations = []
n_chunks = int(len(lats) // BATCH_SIZE) + 1
lat_chunks = np.array_split(lats, n_chunks)
lon_chunks = np.array_split(lons, n_chunks)
for lat_chunk, lon_chunk in zip(lat_chunks, lon_chunks):
latlons = '|'.join(f'{lat},{lon}' for lat, lon in zip(lat_chunk, lon_chunk))
response = requests.post(
'https://api.gpxz.io/v1/elevation/points',
headers={'x-api-key': API_KEY},
data={'latlons': latlons},
)
response.raise_for_status()
elevations += [r['elevation'] for r in response.json()['results']]
return elevations
With these pieces in place, we’ll loop over all the points in the gpxz file, get the elevation for all of them, then save the modified file:
# Find points.
points = list(gpx.walk(only_points=True))
latitudes = [p.latitude for p in points]
longitudes = [p.longitude for p in points]
# Update elevations.
elevations = gpxz_elevation(latitudes, longitudes)
for point, elevation in zip(points, elevations):
point.elevation = elevation
# Save gpx file.
with open('run.with_elevation.gpx', 'w') as f:
f.write(gpx.to_xml())
The full code looks like
import gpxpy
import numpy as np
import requests
API_KEY = 'ak_demo_1234' # Get a free key from www.gpxz.io.
BATCH_SIZE = 50 # 512 for paid customers.
def gpxz_elevation(lats, lons):
'''Iterate over the coordinates in chunks, querying the GPXZ api to return
a list of elevations in the same order.'''
elevations = []
n_chunks = int(len(lats) // BATCH_SIZE) + 1
lat_chunks = np.array_split(lats, n_chunks)
lon_chunks = np.array_split(lons, n_chunks)
for lat_chunk, lon_chunk in zip(lat_chunks, lon_chunks):
latlons = '|'.join(f'{lat},{lon}' for lat, lon in zip(lat_chunk, lon_chunk))
response = requests.post(
'https://api.gpxz.io/v1/elevation/points',
headers={'x-api-key': API_KEY},
data={'latlons': latlons},
)
response.raise_for_status()
elevations += [r['elevation'] for r in response.json()['results']]
return elevations
def add_elevation_to_gpx(gpx_path, output_path='with_elevation.gpx'):
# Load gpx.
gpx_path = 'run.gpx'
with open(gpx_path) as f:
gpx = gpxpy.parse(f)
# Find points.
points = list(gpx.walk(only_points=True))
latitudes = [p.latitude for p in points]
longitudes = [p.longitude for p in points]
# Update elevations.
elevations = gpxz_elevation(latitudes, longitudes)
for point, elevation in zip(points, elevations):
point.elevation = elevation
# Save gpx file.
with open(output_path, 'w') as f:
f.write(gpx.to_xml())