About

A map of the trails in Pacific Spirit Regional Park (Vancouver, BC) and the streets around it, built for a running group. It’s the first step towards generating routes: turning written directions into GPX and back, making loops or point-to-point runs of a given distance, and one day covering every trail in the park with as little backtracking as possible.

Reading the map

  • Park trails are coloured by bike access, the same split as the official park map: ■ shared, ■ hiking only, ■ not tagged either way.
  • Grey lines are streets, sidewalks and paths outside the park. We use them sparingly, only to link trails without doubling back.
  • ● WC marks toilets and ● W drinking water, useful when planning long runs and aid stations. They come from OpenStreetMap: check opening hours and seasonal closures before race day.
  • Hover a line for its name. Click it for its tags and a link to it on OpenStreetMap.
  • The buttons at the top left turn layers on and off, including what we removed from the network (red) and links we added (dotted black).

On a phone, the full-screen map is easier to use.

Routes

The route of the day (ROTD) page picks a loop (or out-and-back) from the Park Centre at the distance you choose: the same route of the day for everyone, or shuffle for another. Routes lists every route by its number, to filter and pick from. Each comes with an elevation profile, written directions, a GPX file, an image for sharing, and palm-size cards to print for anyone running without a device (or to save as a picture on your phone). Toilets and water are always on the route map, and marked on the elevation profile where the route passes them (within 40 m). The Ultra page is one route over every trail in the park. Bikes does the same for bikes: rides of 8–24 km and one ride over every trail open to them, leaving out the hiking-only trails.

How routes are made and picked

The routes are made ahead of time, not on your phone. Each time the site is built, a script (07_generate_routes.py) tries about 13,500 loops from the Park Centre: out to a random trail junction A, across to another, B, and back. Each leg takes the cheapest way, where a metre of street costs 3× a metre of trail, and ground already covered costs 8× more, so legs look for new trail. Those “random” junctions come from a fixed seed, so the same data always gives the same routes. Out-and-backs go to a trail junction at half the distance and back.

Only good routes are kept: at least 80% trail, at most 10% street, and (for loops) at most 15% of the distance run twice.

Then near-copies are removed. Many good loops differ only in a short stretch, so we compare routes by the trail segments they use (a segment is the stretch between two junctions): the segments two routes share, divided by all the segments either one uses. The best routes go first (most trail, least doubling back, fewest streets), and each next route is kept only if it shares at most 75% with every route already kept. So any two routes differ in at least a quarter of their trail, about 2 km on an 8 km loop. Allowing more overlap would give more routes, but more of them would feel the same.

Route numbers are positions in that list. Loops are sorted shortest first and numbered L01, L02, … (L01 is the shortest loop); out-and-backs are OB01, OB02, …. When the data or our edits change and the site is rebuilt, the list can change, so a number isn’t a permanent name for a route.

The 7–10 km routes cover the park. Random turn points tend to miss trails close to the start and at the far north end, so after the library is built the script adds loops aimed at whatever trail no 7–10 km route runs yet, until no more good loops can be found:

Just for fun, here’s what they miss. Each bit is too short, or too awkward to reach, for a good loop, but it’s only a few metres if you want to say you’ve run the lot. “Map” opens the trail map right there.

The Ultra page covers everything, these bits included.

The route of the day is a shuffle seeded by the date. The page keeps the routes that match your choices (type and distance), then shuffles them using today’s date in Vancouver (e.g. 2026-09-27) as the seed for its random numbers. The same seed always gives the same shuffle, so everyone who picks the same settings on the same day sees the same order:

  • the first route in today’s order is the route of the day, and it’s what you get when the page loads or you refresh;
  • Another route moves to the next one in the order, so you won’t see a repeat until you’ve been through every match;
  • at midnight the date changes, and so does the order;
  • different settings give a different list to shuffle, and so a different route.

Nothing is stored or sent anywhere: every phone does the same arithmetic. “1 of 42 matching routes” means 42 routes fit your settings, and you’re on the first in today’s order.

Sharing a route. Share opens your phone’s share sheet with the route’s name, distance, climb, written directions and a link. On a computer the button is Copy link: it copies the same text, ready to paste into an email or a message. The link ends in ?route= and a short code that fingerprints the route’s exact path, so it opens that same route (not the route of the day) for whoever you send it to. Where the phone allows, the GPX file goes along too. If the routes have been rebuilt since and that exact route is gone, the link falls back to the route of the day.

Our edits

OpenStreetMap has more than we run and misses a few links we use, so we keep a small set of edits:

  • NW and SW Marine Drive are the park’s edge for us: beach and foreshore trails below them are left out, as are a few dead-end stubs, parking-lot spurs and school grounds.
  • Where a trail crosses W 16th Avenue but OSM never joined the two sides (Cleveland Trail; Sword Fern to Douglas Fir), we add the missing link.
  • Camosun Trails’ last stretch up to W 16th Ave counts as park trail, although it’s just outside the official park boundary.
  • Every build checks that trails still connect across Chancellor Blvd, University Blvd and W 16th Ave.

Data

Data Source Licence
Trails, streets, toilets, water OpenStreetMap © OpenStreetMap contributors, ODbL
Elevation NRCan High Resolution DEM (lidar, 2 m) Open Government Licence – Canada
Park boundary Metro Vancouver open data Open Government Licence – Metro Vancouver
Basemap OpenFreeMap © OpenMapTiles, data © OpenStreetMap contributors

If a public trail is missing or wrong, the best fix is in OpenStreetMap itself, so every map and app gets it. The code is on GitHub.

Please stay on marked trails and follow the park’s rules on bikes and dogs.