The Tour de Los Padres is coming! The race organizer post the route on ridewithgps. This works, but has convoluted interfaces for people not wanting to use their service. I just wrote a simple script to export their data into a plain .gpx file, including all the waypoints; their exporter omits those.

I've seen two flavors of their data, so here're two flavors of the gpx-from-ridewithgps.py script:

#!/usr/bin/python3
import sys
import json

def quote_xml(s):
    return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")

print("Reading stdin", file=sys.stderr)
data = json.load(sys.stdin)

print(r"""<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="gpx-from-ridewithgps.py" xmlns="http://www.topografix.com/GPX/1/1">""")

for item in data["extras"]:
    if item["type"] != "point_of_interest":
        continue
    poi = item["point_of_interest"]
    print(f'  <wpt lat="{poi["lat"]}" lon="{poi["lng"]}">')
    print(f'    <name>{quote_xml(poi["name"])}</name>')

    desc = poi.get("description","")
    if len(desc):
        print(f'    <desc>{quote_xml(desc)}</desc>')
    print(f'  </wpt>')

print("  <trk><trkseg>")
for pt in data.get("route", {}).get("track_points", []):
    print(f'    <trkpt lat="{pt["y"]}" lon="{pt["x"]}"><ele>{pt["e"]}</ele></trkpt>')
print("  </trkseg></trk>")

print("</gpx>")
#!/usr/bin/python3
import sys
import json

def quote_xml(s):
    return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")

print("Reading stdin", file=sys.stderr)
data = json.load(sys.stdin)

print(r"""<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="gpx-from-ridewithgps.py" xmlns="http://www.topografix.com/GPX/1/1">""")

for poi in data["points_of_interest"]:
    print(f'  <wpt lat="{poi["lat"]}" lon="{poi["lng"]}">')
    print(f'    <name>{quote_xml(poi["name"])}</name>')

    desc = poi.get("description","")
    if len(desc):
        print(f'    <desc>{quote_xml(desc)}</desc>')
    print(f'  </wpt>')

for poi in data["course_points"]:
    print(f'  <wpt lat="{poi["y"]}" lon="{poi["x"]}">')
    print(f'    <name>{quote_xml(poi["n"])}</name>')
    print(f'  </wpt>')

print("  <trk><trkseg>")
for pt in data['track_points']:
    print(f'    <trkpt lat="{pt["y"]}" lon="{pt["x"]}"><ele>{pt["e"]}</ele></trkpt>')
print("  </trkseg></trk>")

print("</gpx>")

You invoke it by downloading the route and feeding it into the script:

curl -s https://ridewithgps.com/routes/54493422.json | ./ridewithgps-to-gpx.py > out.gpx

Note that the route number 54493422 is in the url above.