Hosting GraphHopper on Heroku

My project to virtually circumnavigate Lake Superior is now complete. At some point during the project, however, I noticed that the site was no longer showing the distance remaining nor the likely route from the last end-point to the original starting point. Since the mapping was done via Leaflet, I was using an add-on called Leaflet Routing Machine to find the remaining distance and route. It turns out that the default (and free) routing service used by the add-on (OSRM demo server) was no longer available. It was really just meant for testing purposes anyway. The alternatives, however, were fee-based and I certainly didn’t need that level of service.

One of the alternatives available to Leaflet Routing Machine is GraphHopper. Although they offer a fee-based service, it’s also available for download and can be easily set up to run locally. I was able to get it up-and-running on my development environment (Fedora 31) within minutes and with a simple change to the routing configuration, my routing was working perfectly. The problem, however, is that GraphHopper is written in Java and the service where my project (virtualbicycletours.com) is hosted doesn’t support Tomcat or Java apps.

Enter Heroku.

Heroku is a cloud platform that makes it very easy to build, deliver, monitor and scale apps. They offer a free tier that gives up to 1000 dyno hours per month which should be quite sufficient for my use. The dyno will sleep after 30 minutes of inactivity, is only allocated 512 MB or RAM, and only gets 1 worker. The challenge would be keeping the app’s footprint small enough to use the free service.

Basic Steps to Implementation

  • Create Heroku account
  • Clone GraphHopper repository
  • Download needed OSM files
  • Build PBF using osmconvert
  • Update config.yml
  • Test local installation
  • Install Heroku CLI tool
  • Initialize and Deploy to Heroku
  • Test Dyno
  • Update Leaflet Routing Machine configuration

Create Heroku Account

You’ll obviously need an account at Heroku. Even though you’re signing up for the free tier, they still require the account to be verified via a credit card. A free dyno gets 1000 hours/month which is more than enough for my purposes.

Clone GraphHopper Repository

git clone git://github.com/graphhopper/graphhopper.git

Download OSM Files

Although the graphhopper.sh script can be used to pull in a particular region, if you need to create a very specific routing area, you’ll need to download the necessary OSM files and build it yourself. Visit the Geofabrik Download Server and grab the OSM files that encompass the region you’ll be routing within. Since I’m routing around the Great Lakes, I downloaded the 8 states that border the lakes plus Ontario.

Build PBF Using osmconvert

Now that you’ve got the necessary OSM files, they’ll need to be combined into a single PBF using the osmconvert tool. I grabbed the 64bit Linux binary.

Combining the 9 OSM files can be done by running:

./osmconvert64  *-latest.osm -o=lakesuperior.pbf

Unfortunately, even with only 9 states plus Ontario, the resulting Heroku slug exceeds the 500MB maximum allowed by the free tier. In order to reduce it, I used the bounding polygon option of osmconvert. The easiest way to draw the polygon was with the polylines site at doogal.co.uk. I got the coordinates from the CSV tab and then did a bit of manipulation in order to get them in the format described on the Osmosis/Polygon Filter File Format page. Now run osmconvert64 with the “-B” option.

./osmconvert64 *-latest.osm -v -B=greatlakes.poly --complete-ways --complete-multipolygons -o=greatlakes.pbf

This reduced the resulting PBF file to a bit over 600MB. Keep in mind that the initial run of the Graphhopper JAR will generate the graph data (located in the directory defined by graph.location noted below). The contents of this directory is what will be included in the Heroku slug (not the PBF). In my case, the graph directory was just a bit over 300MB.

Update config.yml

datareader.file: greatlakes.pbf
graph.location: greatlakes

Test Local Installation

export JAVA_OPTS="-Xmx3g -Xms3g"
java -jar web/target/graphhopper-web-2.0-SNAPSHOT.jar \
server config.yml

You can now test your local installation with:

http://localhost:8989/info
http://localhost:8989/maps/

Install Heroku CLI Tool

Although not particularly recommended, I installed the Heroku CLI via NPM. Other methods are described in their documentation.

Initialize and Deploy to Heroku

More details can be found in the Deploying with Git document, but it basically goes as follows:

git add .
git commit -am "first commit"
heroku login
heroku create
git push heroku master

Test Dyno

As shown above, my Heroku dyno was given the name “protected-mesa-81611”, so the running dyno can be tested at:

test web at http://protected-mesa-81611.herokuapp.com/maps/
test api at https://protected-mesa-81611.herokuapp.com/info
test routing at http://protected-mesa-81611.herokuapp.com/maps/?point=duluth&point=grand%20marais&locale=en-US&vehicle=car&weighting=fastest&elevation=false&turn_costs=false&use_miles=false&layer=OpenStreetMap

Update Leaflet Routing Machine Config

let rControl = L.Routing.control({
router: L.Routing.graphHopper(undefined, {
serviceUrl: 'https://protected-mesa-81611.herokuapp.com/route'
}),

One Last Thing…

As noted earlier, the free Heroku account will put the dyno to sleep after 30 minutes of inactivity. Depending on the size of the application, there can be a significant delay, sometimes as much as 30 seconds, before the app respond. You can cut down some of this delay by calling the dyno at the earliest possible opportunity so that it might be ready by the time the real routing call is made. Even a couple of seconds helps.

I hope you found this information useful. If you have any questions or comments, feel free to post them below.

Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.